kumade 0.4.0 → 0.5.0

Sign up to get free protection for your applications and to get access to all the features.
Files changed (41) hide show
  1. data/README.md +9 -1
  2. data/features/kumade_executable.feature +11 -12
  3. data/features/kumade_without_jammit.feature +2 -2
  4. data/kumade.gemspec +1 -0
  5. data/lib/kumade.rb +7 -1
  6. data/lib/kumade/cli.rb +4 -4
  7. data/lib/kumade/command_line.rb +33 -0
  8. data/lib/kumade/configuration.rb +13 -6
  9. data/lib/kumade/deployer.rb +9 -9
  10. data/lib/kumade/git.rb +25 -20
  11. data/lib/kumade/heroku.rb +18 -11
  12. data/lib/kumade/outputter.rb +21 -0
  13. data/lib/kumade/packager.rb +23 -76
  14. data/lib/kumade/packager_list.rb +29 -0
  15. data/lib/kumade/packagers/jammit_packager.rb +20 -0
  16. data/lib/kumade/packagers/more_packager.rb +20 -0
  17. data/lib/kumade/packagers/noop_packager.rb +14 -0
  18. data/lib/kumade/rake_task_runner.rb +29 -0
  19. data/lib/kumade/version.rb +1 -1
  20. data/spec/kumade/cli_spec.rb +5 -5
  21. data/spec/kumade/command_line_spec.rb +107 -0
  22. data/spec/kumade/configuration_spec.rb +15 -2
  23. data/spec/kumade/deployer_spec.rb +39 -25
  24. data/spec/kumade/git_spec.rb +168 -57
  25. data/spec/kumade/heroku_spec.rb +19 -25
  26. data/spec/kumade/outputter_spec.rb +50 -0
  27. data/spec/kumade/packager_list_spec.rb +31 -0
  28. data/spec/kumade/packager_spec.rb +66 -275
  29. data/spec/kumade/packagers/jammit_packager_spec.rb +27 -0
  30. data/spec/kumade/packagers/more_packager_spec.rb +37 -0
  31. data/spec/kumade/packagers/noop_packager_spec.rb +9 -0
  32. data/spec/kumade/rake_task_runner_spec.rb +75 -0
  33. data/spec/spec_helper.rb +13 -2
  34. data/spec/support/define_constant.rb +41 -0
  35. data/spec/support/environments.rb +32 -0
  36. data/spec/support/git.rb +5 -0
  37. data/spec/support/heroku.rb +6 -6
  38. data/spec/support/shared_examples/packager.rb +6 -0
  39. metadata +65 -28
  40. data/lib/kumade/base.rb +0 -35
  41. data/spec/kumade/base_spec.rb +0 -99
data/README.md CHANGED
@@ -85,6 +85,14 @@ You can hook in any custom code you want to run there before deploying!
85
85
  Kumade ([pronunciation here](http://translate.google.com/#ja|en|熊手)) means
86
86
  "bamboo rake" in Japanese.
87
87
 
88
+ ## Credits
89
+
90
+ ![thoughtbot](http://thoughtbot.com/images/tm/logo.png)
91
+
92
+ Kumade is maintained and funded by [thoughtbot, inc](http://thoughtbot.com/community)
93
+
94
+ The names and logos for thoughtbot are trademarks of thoughtbot, inc.
95
+
88
96
  ## License
89
97
 
90
- kumade is Copyright © thoughtbot. It is free software, and may be redistributed under the terms specified in the LICENSE file.
98
+ Kumade is Copyright © 2011 thoughtbot. It is free software, and may be redistributed under the terms specified in the LICENSE file.
@@ -21,17 +21,16 @@ Feature: Kumade executable
21
21
  And the output should contain:
22
22
  """
23
23
  ==> Git repo is clean
24
- ==> Packaged assets with Jammit
25
- run git push origin master
24
+ ==> Packaged with Kumade::JammitPackager
25
+ git push origin master
26
26
  ==> Pushed master -> origin
27
- run git branch deploy
28
- run git push -f pretend-staging deploy:master
27
+ git branch deploy >/dev/null
28
+ git push -f pretend-staging deploy:master
29
29
  ==> Pushed deploy:master -> pretend-staging
30
30
  ==> Migrated pretend-staging
31
- run git checkout master && git branch -D deploy
32
31
  ==> Deployed to: pretend-staging
33
32
  """
34
- But the output should not contain "==> Packaged assets with More"
33
+ But the output should not contain "==> Packaged with Kumade::MorePackager"
35
34
 
36
35
  Scenario: Default environment is staging
37
36
  When I run kumade with "-p"
@@ -52,14 +51,13 @@ Feature: Kumade executable
52
51
  Then the output should contain:
53
52
  """
54
53
  ==> Git repo is clean
55
- ==> Packaged assets with Jammit
56
- run git push origin new_branch
54
+ ==> Packaged with Kumade::JammitPackager
55
+ git push origin new_branch
57
56
  ==> Pushed new_branch -> origin
58
- run git branch deploy
59
- run git push -f pretend-staging deploy:master
57
+ git branch deploy >/dev/null
58
+ git push -f pretend-staging deploy:master
60
59
  ==> Pushed deploy:master -> pretend-staging
61
60
  ==> Migrated pretend-staging
62
- run git checkout new_branch && git branch -D deploy
63
61
  ==> Deployed to: pretend-staging
64
62
  """
65
63
 
@@ -95,4 +93,5 @@ Feature: Kumade executable
95
93
  end
96
94
  """
97
95
  When I run kumade with "pretend-staging -p"
98
- Then the output should match /kumade:before_asset_compilation.*Packaged assets with Jammit/
96
+ Then the output should contain "kumade:before_asset_compilation"
97
+ And the output should contain "==> Packaged with Kumade::JammitPackager"
@@ -23,7 +23,7 @@ Feature: Kumade without jammit
23
23
  end
24
24
  """
25
25
  When I run kumade with "pretend-staging"
26
- Then the output should contain "Running kumade:before_asset_compilation task"
26
+ Then the output should contain "Running rake task: kumade:before_asset_compilation"
27
27
  And the output should contain "Hi!"
28
28
 
29
29
  Scenario: Don't run rake task in pretend mode
@@ -36,5 +36,5 @@ Feature: Kumade without jammit
36
36
  end
37
37
  """
38
38
  When I run kumade with "pretend-staging -p"
39
- Then the output should contain "Running kumade:before_asset_compilation task"
39
+ Then the output should contain "Running rake task: kumade:before_asset_compilation"
40
40
  And the output should not contain "Hi!"
@@ -26,5 +26,6 @@ Gem::Specification.new do |s|
26
26
  s.add_development_dependency('cucumber', '~> 1.0.2')
27
27
  s.add_development_dependency('aruba', '~> 0.4.3')
28
28
  s.add_development_dependency('jammit', '~> 0.6.3')
29
+ s.add_development_dependency('less', '~> 2.0')
29
30
  s.add_development_dependency('bourne')
30
31
  end
@@ -1,5 +1,4 @@
1
1
  module Kumade
2
- autoload :Base, "kumade/base"
3
2
  autoload :Git, "kumade/git"
4
3
  autoload :Deployer, "kumade/deployer"
5
4
  autoload :CLI, "kumade/cli"
@@ -8,6 +7,13 @@ module Kumade
8
7
  autoload :Configuration, "kumade/configuration"
9
8
  autoload :Heroku, "kumade/heroku"
10
9
  autoload :Packager, "kumade/packager"
10
+ autoload :MorePackager, "kumade/packagers/more_packager"
11
+ autoload :JammitPackager, "kumade/packagers/jammit_packager"
12
+ autoload :NoopPackager, "kumade/packagers/noop_packager"
13
+ autoload :PackagerList, "kumade/packager_list"
14
+ autoload :RakeTaskRunner, "kumade/rake_task_runner"
15
+ autoload :CommandLine, "kumade/command_line"
16
+ autoload :Outputter, "kumade/outputter"
11
17
 
12
18
  def self.configuration
13
19
  @@configuration ||= Configuration.new
@@ -16,7 +16,7 @@ module Kumade
16
16
  parse_arguments!(args)
17
17
 
18
18
  Kumade.configuration.pretending = !!@options[:pretend]
19
- Kumade.configuration.environment = args.shift || 'staging'
19
+ Kumade.configuration.environment = args.shift
20
20
 
21
21
  self.class.swapping_stdout_for(out, print_output?) do
22
22
  deploy
@@ -44,11 +44,11 @@ module Kumade
44
44
 
45
45
  def deploy
46
46
  if Kumade.configuration.pretending?
47
- puts "==> In Pretend Mode"
47
+ Kumade.configuration.outputter.info("In Pretend Mode")
48
48
  end
49
- puts "==> Deploying to: #{Kumade.configuration.environment}"
49
+ Kumade.configuration.outputter.info("Deploying to: #{Kumade.configuration.environment}")
50
50
  self.class.deployer.new.deploy
51
- puts "==> Deployed to: #{Kumade.configuration.environment}"
51
+ Kumade.configuration.outputter.info("Deployed to: #{Kumade.configuration.environment}")
52
52
  end
53
53
 
54
54
  def parse_arguments!(args)
@@ -0,0 +1,33 @@
1
+ require 'cocaine'
2
+
3
+ module Kumade
4
+ class CommandLine
5
+ def initialize(command_to_run)
6
+ @command_line = Cocaine::CommandLine.new(command_to_run)
7
+ end
8
+
9
+ def run_or_error(error_message = nil)
10
+ run_with_status || Kumade.configuration.outputter.error(error_message)
11
+ end
12
+
13
+ def run_with_status
14
+ Kumade.configuration.outputter.say_command(command)
15
+ Kumade.configuration.pretending? || run
16
+ end
17
+
18
+ def run
19
+ begin
20
+ @command_line.run
21
+ true
22
+ rescue Cocaine::ExitStatusError, Cocaine::CommandNotFoundError
23
+ false
24
+ end
25
+ end
26
+
27
+ private
28
+
29
+ def command
30
+ @command_line.command
31
+ end
32
+ end
33
+ end
@@ -1,14 +1,21 @@
1
1
  module Kumade
2
2
  class Configuration
3
- def initialize(environment = 'staging', pretending = false )
4
- @environment = environment
5
- @pretending = pretending
6
- end
3
+ attr_writer :pretending, :environment
7
4
 
8
5
  def pretending?
9
- @pretending
6
+ @pretending || false
7
+ end
8
+
9
+ def environment
10
+ @environment || "staging"
10
11
  end
11
12
 
12
- attr_accessor :pretending, :environment
13
+ def outputter
14
+ @outputter ||= Outputter.new
15
+ end
16
+
17
+ def outputter=(new_outputter)
18
+ @outputter = new_outputter
19
+ end
13
20
  end
14
21
  end
@@ -2,11 +2,10 @@ require "rake"
2
2
  require 'cocaine'
3
3
 
4
4
  module Kumade
5
- class Deployer < Base
5
+ class Deployer
6
6
  attr_reader :git, :heroku, :packager
7
7
 
8
8
  def initialize
9
- super()
10
9
  @git = Git.new
11
10
  @heroku = Heroku.new
12
11
  @branch = @git.current_branch
@@ -19,7 +18,8 @@ module Kumade
19
18
  pre_deploy
20
19
  heroku.sync
21
20
  heroku.migrate_database
22
- rescue
21
+ rescue => deploying_error
22
+ Kumade.configuration.outputter.error("#{deploying_error.class}: #{deploying_error.message}")
23
23
  ensure
24
24
  post_deploy
25
25
  end
@@ -28,14 +28,14 @@ module Kumade
28
28
  def pre_deploy
29
29
  ensure_clean_git
30
30
  package_assets
31
- sync_github
31
+ sync_origin
32
32
  end
33
33
 
34
34
  def package_assets
35
- packager.run
35
+ @packager.run
36
36
  end
37
37
 
38
- def sync_github
38
+ def sync_origin
39
39
  git.push(@branch)
40
40
  end
41
41
 
@@ -50,12 +50,12 @@ module Kumade
50
50
  def ensure_heroku_remote_exists
51
51
  if git.remote_exists?(Kumade.configuration.environment)
52
52
  if git.heroku_remote?
53
- success("#{Kumade.configuration.environment} is a Heroku remote")
53
+ Kumade.configuration.outputter.success("#{Kumade.configuration.environment} is a Heroku remote")
54
54
  else
55
- error(%{Cannot deploy: "#{Kumade.configuration.environment}" remote does not point to Heroku})
55
+ Kumade.configuration.outputter.error(%{Cannot deploy: "#{Kumade.configuration.environment}" remote does not point to Heroku})
56
56
  end
57
57
  else
58
- error(%{Cannot deploy: "#{Kumade.configuration.environment}" remote does not exist})
58
+ Kumade.configuration.outputter.error(%{Cannot deploy: "#{Kumade.configuration.environment}" remote does not exist})
59
59
  end
60
60
  end
61
61
  end
@@ -1,10 +1,6 @@
1
1
  require 'cocaine'
2
2
  module Kumade
3
- class Git < Base
4
- def initialize
5
- super()
6
- end
7
-
3
+ class Git
8
4
  def heroku_remote?
9
5
  remote_url = `git config --get remote.#{Kumade.configuration.environment}.url`.strip
10
6
  !! remote_url.strip.match(/^git@heroku\..+:(.+)\.git$/)
@@ -20,25 +16,32 @@ module Kumade
20
16
  command << remote
21
17
  command << branch
22
18
  command = command.join(" ")
23
- run_or_error(command, "Failed to push #{branch} -> #{remote}")
24
- success("Pushed #{branch} -> #{remote}")
19
+
20
+ command_line = CommandLine.new(command)
21
+ command_line.run_or_error("Failed to push #{branch} -> #{remote}")
22
+ Kumade.configuration.outputter.success("Pushed #{branch} -> #{remote}")
25
23
  end
26
24
 
27
25
  def create(branch)
28
- unless branch_exist?(branch)
29
- run_or_error("git branch #{branch}", "Failed to create #{branch}")
26
+ unless has_branch?(branch)
27
+ CommandLine.new("git branch #{branch} >/dev/null").run_or_error("Failed to create #{branch}")
30
28
  end
31
29
  end
32
30
 
33
31
  def delete(branch_to_delete, branch_to_checkout)
34
- run_or_error("git checkout #{branch_to_checkout} && git branch -D #{branch_to_delete}",
35
- "Failed to clean up #{branch_to_delete} branch")
32
+ if has_branch?(branch_to_delete)
33
+ command_line = CommandLine.new("git checkout #{branch_to_checkout} 2>/dev/null && git branch -D #{branch_to_delete}")
34
+ command_line.run_or_error("Failed to clean up #{branch_to_delete} branch")
35
+ end
36
36
  end
37
37
 
38
- def add_and_commit_all_in(dir, branch, commit_message, success_output, error_output)
39
- run_or_error "git checkout -b #{branch} && git add -f #{dir} && git commit -m '#{commit_message}'",
40
- "Cannot deploy: #{error_output}"
41
- success success_output
38
+ def add_and_commit_all_assets_in(dir)
39
+ command = ["git checkout -b #{Kumade::Heroku::DEPLOY_BRANCH} 2>/dev/null",
40
+ "git add -f #{dir}",
41
+ "git commit -m 'Compiled assets.'"].join(' && ')
42
+ command_line = CommandLine.new(command)
43
+ command_line.run_or_error("Cannot deploy: couldn't commit assets")
44
+ Kumade.configuration.outputter.success("Added and committed all assets")
42
45
  end
43
46
 
44
47
  def current_branch
@@ -54,19 +57,21 @@ module Kumade
54
57
  end
55
58
 
56
59
  def dirty?
57
- !run("git diff --exit-code")
60
+ ! CommandLine.new("git diff --exit-code").run
58
61
  end
59
62
 
60
63
  def ensure_clean_git
61
64
  if ! Kumade.configuration.pretending? && dirty?
62
- error("Cannot deploy: repo is not clean.")
65
+ Kumade.configuration.outputter.error("Cannot deploy: repo is not clean.")
63
66
  else
64
- success("Git repo is clean")
67
+ Kumade.configuration.outputter.success("Git repo is clean")
65
68
  end
66
69
  end
67
70
 
68
- def branch_exist?(branch)
69
- run("git show-ref #{branch}")
71
+ private
72
+
73
+ def has_branch?(branch)
74
+ CommandLine.new("git show-ref #{branch}").run
70
75
  end
71
76
  end
72
77
  end
@@ -1,12 +1,11 @@
1
1
  require 'cocaine'
2
2
 
3
3
  module Kumade
4
- class Heroku < Base
4
+ class Heroku
5
5
  DEPLOY_BRANCH = "deploy"
6
6
  attr_reader :git
7
7
 
8
8
  def initialize
9
- super()
10
9
  @git = Git.new
11
10
  @branch = @git.current_branch
12
11
  end
@@ -18,7 +17,7 @@ module Kumade
18
17
 
19
18
  def migrate_database
20
19
  heroku("rake db:migrate") unless Kumade.configuration.pretending?
21
- success("Migrated #{Kumade.configuration.environment}")
20
+ Kumade.configuration.outputter.success("Migrated #{Kumade.configuration.environment}")
22
21
  end
23
22
 
24
23
  def delete_deploy_branch
@@ -26,21 +25,29 @@ module Kumade
26
25
  end
27
26
 
28
27
  def heroku(command)
29
- heroku_command = if cedar?
30
- "bundle exec heroku run"
31
- else
32
- "bundle exec heroku"
33
- end
34
- run_or_error("#{heroku_command} #{command} --remote #{Kumade.configuration.environment}",
35
- "Failed to run #{command} on Heroku")
28
+ full_heroku_command = "#{bundle_exec_heroku} #{command} --remote #{Kumade.configuration.environment}"
29
+ command_line = CommandLine.new(full_heroku_command)
30
+ command_line.run_or_error("Failed to run #{command} on Heroku")
36
31
  end
37
32
 
38
33
  def cedar?
39
34
  return @cedar unless @cedar.nil?
40
35
 
41
- @cedar = Cocaine::CommandLine.new("bundle exec heroku stack --remote #{Kumade.configuration.environment}").run.split("\n").grep(/\*/).any? do |line|
36
+ command_line = CommandLine.new("bundle exec heroku stack --remote #{Kumade.configuration.environment}")
37
+
38
+ @cedar = command_line.run_or_error.split("\n").grep(/\*/).any? do |line|
42
39
  line.include?("cedar")
43
40
  end
44
41
  end
42
+
43
+ private
44
+
45
+ def bundle_exec_heroku
46
+ if cedar?
47
+ "bundle exec heroku run"
48
+ else
49
+ "bundle exec heroku"
50
+ end
51
+ end
45
52
  end
46
53
  end
@@ -0,0 +1,21 @@
1
+ module Kumade
2
+ class Outputter
3
+ def success(message)
4
+ puts "==> #{message}"
5
+ end
6
+
7
+ def info(message)
8
+ puts "==> #{message}"
9
+ end
10
+
11
+ def error(message)
12
+ puts "==> ! #{message}"
13
+ raise Kumade::DeploymentError, message
14
+ end
15
+
16
+ def say_command(command)
17
+ prefix = " " * 8
18
+ puts "#{prefix}#{command}"
19
+ end
20
+ end
21
+ end
@@ -1,94 +1,41 @@
1
1
  module Kumade
2
- class Packager < Base
3
- attr_reader :git
4
-
5
- def initialize(git)
6
- super()
7
- @git = git
2
+ class Packager
3
+ def initialize(git, packager = Packager.available_packager)
4
+ @packager = packager
5
+ @git = git
8
6
  end
9
7
 
10
8
  def run
11
- invoke_custom_task if custom_task?
12
- package_with_jammit if jammit_installed?
13
- package_with_more if more_installed?
14
- end
15
-
16
- def invoke_custom_task
17
- success "Running kumade:before_asset_compilation task"
18
- Rake::Task["kumade:before_asset_compilation"].invoke unless Kumade.configuration.pretending?
9
+ precompile_assets
10
+ package
19
11
  end
20
12
 
21
- def custom_task?
22
- load("Rakefile") if File.exist?("Rakefile")
23
- Rake::Task.task_defined?("kumade:before_asset_compilation")
13
+ def self.available_packager
14
+ Kumade::PackagerList.new.first
24
15
  end
25
16
 
26
- def package_with_jammit
27
- begin
28
- success_message = "Packaged assets with Jammit"
29
-
30
- if Kumade.configuration.pretending?
31
- success(success_message)
32
- else
33
- Jammit.package!
17
+ private
34
18
 
35
- success(success_message)
36
- git_add_and_commit_all_assets_in(jammit_assets_path)
37
- end
38
- rescue => jammit_error
39
- error("Error: #{jammit_error.class}: #{jammit_error.message}")
40
- end
19
+ def precompile_assets
20
+ RakeTaskRunner.new("kumade:before_asset_compilation").invoke
41
21
  end
42
22
 
43
- def package_with_more
44
- success_message = "Packaged assets with More"
45
- if Kumade.configuration.pretending?
46
- success(success_message)
47
- else
48
- begin
49
- run "bundle exec rake more:generate"
50
- if git.dirty?
51
- success(success_message)
52
- git_add_and_commit_all_assets_in(more_assets_path)
53
- end
54
- rescue => more_error
55
- error("Error: #{more_error.class}: #{more_error.message}")
23
+ def package
24
+ return Kumade.configuration.outputter.success(success_message) if Kumade.configuration.pretending?
25
+
26
+ begin
27
+ @packager.package
28
+ if @git.dirty?
29
+ @git.add_and_commit_all_assets_in(@packager.assets_path)
30
+ Kumade.configuration.outputter.success(success_message)
56
31
  end
32
+ rescue => packager_exception
33
+ Kumade.configuration.outputter.error("Error: #{packager_exception.class}: #{packager_exception.message}")
57
34
  end
58
35
  end
59
36
 
60
- def git_add_and_commit_all_assets_in(dir)
61
- git.add_and_commit_all_in(dir, Kumade::Heroku::DEPLOY_BRANCH, 'Compiled assets', "Added and committed all assets", "couldn't commit assets")
62
- end
63
-
64
- def jammit_assets_path
65
- File.join(Jammit::PUBLIC_ROOT, Jammit.package_path)
66
- end
67
-
68
- def more_assets_path
69
- File.join('public', ::Less::More.destination_path)
70
- end
71
-
72
- def jammit_installed?
73
- @jammit_installed ||=
74
- (defined?(Jammit) ||
75
- begin
76
- require 'jammit'
77
- true
78
- rescue LoadError
79
- false
80
- end)
81
- end
82
-
83
- def more_installed?
84
- @more_installed ||=
85
- (defined?(Less::More) ||
86
- begin
87
- require 'less/more'
88
- true
89
- rescue LoadError
90
- false
91
- end)
37
+ def success_message
38
+ "Packaged with #{@packager.name}"
92
39
  end
93
40
  end
94
41
  end