blazing 0.0.16 → 0.1.0.alpha1

Sign up to get free protection for your applications and to get access to all the features.
Files changed (54) hide show
  1. data/.gitignore +2 -0
  2. data/.rvmrc +1 -1
  3. data/Gemfile +0 -10
  4. data/Gemfile.lock +53 -44
  5. data/Guardfile +8 -0
  6. data/README.md +23 -99
  7. data/Rakefile +21 -4
  8. data/bin/blazing +23 -5
  9. data/blazing.gemspec +19 -8
  10. data/features/help_banner.feature +13 -0
  11. data/features/step_definitions/blazing_steps.rb +1 -0
  12. data/features/support/env.rb +9 -0
  13. data/lib/blazing.rb +4 -13
  14. data/lib/blazing/config.rb +38 -73
  15. data/lib/blazing/dsl_setter.rb +20 -0
  16. data/lib/blazing/recipe.rb +5 -78
  17. data/lib/blazing/runner.rb +44 -2
  18. data/lib/blazing/shell.rb +7 -0
  19. data/lib/blazing/target.rb +44 -134
  20. data/lib/blazing/templates/config.erb +21 -0
  21. data/lib/blazing/templates/hook.erb +43 -0
  22. data/lib/blazing/version.rb +1 -1
  23. data/spec/blazing/config_spec.rb +104 -48
  24. data/spec/blazing/integration/init_spec.rb +8 -37
  25. data/spec/blazing/integration/setup_local_spec.rb +27 -0
  26. data/spec/blazing/integration/setup_remote_spec.rb +32 -0
  27. data/spec/blazing/integration/update_spec.rb +38 -0
  28. data/spec/blazing/recipe_spec.rb +5 -66
  29. data/spec/blazing/runner_spec.rb +41 -6
  30. data/spec/blazing/target_spec.rb +26 -99
  31. data/spec/spec_helper.rb +18 -12
  32. data/spec/support/{config.rb → empty_config.rb} +0 -0
  33. metadata +170 -97
  34. data/TODO +0 -3
  35. data/lib/blazing/base.rb +0 -41
  36. data/lib/blazing/bootstrap.rb +0 -27
  37. data/lib/blazing/cli/base.rb +0 -64
  38. data/lib/blazing/cli/create.rb +0 -32
  39. data/lib/blazing/cli/hook.rb +0 -22
  40. data/lib/blazing/cli/templates/blazing.tt +0 -7
  41. data/lib/blazing/cli/templates/post-hook.tt +0 -25
  42. data/lib/blazing/core_ext/object.rb +0 -8
  43. data/lib/blazing/logger.rb +0 -31
  44. data/lib/blazing/recipes/bundler_recipe.rb +0 -20
  45. data/lib/blazing/recipes/rvm_recipe.rb +0 -11
  46. data/spec/blazing/binary_spec.rb +0 -11
  47. data/spec/blazing/cli/base_spec.rb +0 -94
  48. data/spec/blazing/cli/create_spec.rb +0 -27
  49. data/spec/blazing/cli/hook_spec.rb +0 -16
  50. data/spec/blazing/logger_spec.rb +0 -50
  51. data/spec/blazing/recipes/bundler_recipe_spec.rb +0 -32
  52. data/spec/blazing/recipes/passenger_recipe_spec.rb +0 -6
  53. data/spec/blazing/recipes/rvm_recipe_spec.rb +0 -11
  54. data/spec/blazing/remote_spec.rb +0 -136
@@ -0,0 +1,9 @@
1
+ require 'aruba/cucumber'
2
+ require 'methadone/cucumber'
3
+
4
+ ENV['PATH'] = "#{File.expand_path(File.dirname(__FILE__) + '/../../bin')}#{File::PATH_SEPARATOR}#{ENV['PATH']}"
5
+
6
+ Before do
7
+ # Using "announce" causes massive warnings on 1.9.2
8
+ @puts = true
9
+ end
@@ -1,18 +1,9 @@
1
- require 'thor'
2
- require 'thor/group'
3
- require 'blazing'
4
- require 'blazing/config'
5
- require 'blazing/runner'
6
- require 'blazing/logger'
7
- require 'blazing/target'
8
- require 'blazing/recipe'
9
- require 'blazing/cli/base'
10
- require 'blazing/cli/create'
11
- require 'blazing/cli/hook'
1
+ require "blazing/version"
12
2
 
13
3
  module Blazing
14
4
 
15
- DIRECTORY = 'config'
16
- CONFIGURATION_FILE = 'config/blazing.rb'
5
+ TEMPLATE_ROOT = File.expand_path(File.dirname(__FILE__) + File.join('/', 'blazing', 'templates'))
6
+ DEFAULT_CONFIG_LOCATION = 'config/blazing.rb'
7
+ TMP_HOOK = '/tmp/post-receive'
17
8
 
18
9
  end
@@ -1,90 +1,55 @@
1
- require 'blazing'
2
1
  require 'blazing/target'
2
+ require 'blazing/recipe'
3
+ require 'blazing/dsl_setter'
3
4
 
4
- module Blazing
5
- class Config
5
+ class Blazing::Config
6
6
 
7
- class << self
7
+ extend Blazing::DSLSetter
8
8
 
9
- #
10
- # Read the Configuration File
11
- #
12
- def read(&block)
13
- config = Blazing::Config.new
14
- config.instance_eval(&block)
9
+ attr_reader :file
10
+ attr_accessor :targets
11
+ dsl_setter :repository, :rvm, :rake
15
12
 
16
- config
17
- end
13
+ class << self
18
14
 
19
- #
20
- # Load configuration file and parse it
21
- #
22
- def parse
23
- read do
24
- instance_eval(File.read(Blazing::CONFIGURATION_FILE))
25
- end
26
- end
27
-
28
- #
29
- # DSL Setter helper method
30
- #
31
- def dsl_setter(*names)
32
- names.each do |name|
33
- class_eval <<-EVAL
34
- def #{name}(value = nil)
35
- if value
36
- instance_variable_set("@#{name}", value)
37
- else
38
- instance_variable_get("@#{name}")
39
- end
40
- end
41
- EVAL
42
- end
43
- end
15
+ def parse(configuration_file = nil)
16
+ config = self.new(configuration_file)
17
+ config.instance_eval(File.read(config.file))
44
18
 
19
+ config
45
20
  end
46
21
 
47
- dsl_setter :repository
48
- attr_accessor :targets, :recipes
22
+ end
49
23
 
50
- def initialize
51
- @targets = []
52
- @recipes = []
53
- end
24
+ def initialize(configuration_file = nil)
25
+ @file = configuration_file || Blazing::DEFAULT_CONFIG_LOCATION
26
+ @targets = []
27
+ @recipes = []
28
+ end
54
29
 
55
- def target(name, options = {})
56
- @targets << Blazing::Target.new(name, options.merge(:config => self))
57
- end
30
+ def target(name, location, options = {})
31
+ raise "Name already taken" if targets.find { |t| t.name == name }
32
+ targets << Blazing::Target.new(name, location, self, options)
33
+ end
58
34
 
59
- def use(name, options = {})
60
- @recipes << Blazing::Recipe.new_recipe_by_name(name, options)
35
+ def recipes(recipes = nil)
36
+ if recipes.kind_of? Symbol
37
+ @recipes << Blazing::Recipe.init_by_name(recipes)
38
+ elsif recipes.kind_of? Array
39
+ @recipes = recipes.map { |r| Blazing::Recipe.init_by_name(r) }
40
+ else
41
+ @recipes
61
42
  end
43
+ end
62
44
 
63
- #
64
- # Determines which target is picked, based on defaults and CLI argument
65
- # If only one target is defined, it is the default one
66
- #
67
- def find_target(target_name = nil)
68
- active_target = nil
69
-
70
- if target_name
71
- active_target = targets.find {|target| target.name == target_name }
72
- end
73
-
74
- if active_target.nil? && targets.size == 1
75
- active_target = targets.first
76
- end
77
-
78
- if active_target.nil?
79
- active_target = targets.find {|target| target.default }
80
- end
81
-
82
- if active_target.nil?
83
- raise 'no target specified and no default targets found'
84
- end
85
-
86
- active_target
45
+ def default_target
46
+ if @targets.size > 1
47
+ default = @targets.find { |t| t.options[:default] == true }
48
+ raise 'could not find default target' unless default
49
+ default
50
+ else
51
+ @targets.first
87
52
  end
88
-
89
53
  end
54
+
90
55
  end
@@ -0,0 +1,20 @@
1
+ module Blazing::DSLSetter
2
+
3
+ #
4
+ # DSL Setter helper method
5
+ #
6
+ def dsl_setter(*names)
7
+ names.each do |name|
8
+ class_eval <<-EVAL
9
+ def #{name}(value = nil)
10
+ if value
11
+ instance_variable_set("@#{name}", value)
12
+ else
13
+ instance_variable_get("@#{name}")
14
+ end
15
+ end
16
+ EVAL
17
+ end
18
+ end
19
+
20
+ end
@@ -1,86 +1,13 @@
1
1
  require 'active_support/inflector'
2
- require 'blazing'
3
- # require 'blazing/logger'
4
2
 
5
- module Blazing
6
- class Recipe
3
+ class Blazing::Recipe
7
4
 
5
+ class << self
8
6
 
9
- # TODO: provide hooks for recipe to use bundle exec
10
-
11
- attr_accessor :name, :options
12
-
13
- def initialize(name, options = {})
14
- @name = name.to_s
15
- @options = options
16
- @logger = options[:_logger] ||= Blazing::Logger.new
17
- @runner = Blazing::Runner.new
18
- end
19
-
20
- def recipe_class
21
- # TODO: Unify naming conventions
22
- # Gem Recipe Naming Convention
23
- ('Blazing::' + @name.to_s.gsub('_','/').camelize).constantize
24
- rescue NameError
25
- begin
26
- # Builtin Recipe Naming Convention
27
- ('Blazing::' + (@name.to_s + '_recipe').camelize).constantize
28
- rescue NameError
29
- @logger.log :error, "unable to load #{@name} recipe"
30
- return nil
31
- end
7
+ def init_by_name(name)
8
+ "Blazing::Recipe::#{name.to_s.camelize}".constantize.new
32
9
  end
33
10
 
34
- def run
35
- raise 'run method must be implemented in recipe'
36
- end
37
-
38
- class << self
39
-
40
- def new_recipe_by_name(name, options = {})
41
- load_builtin_recipes
42
- load_gem_recipes
43
- new(name, options).recipe_class.new(name, options)
44
- end
45
-
46
- def load_builtin_recipes
47
- dir = File.join(File.dirname(__FILE__), "/recipes")
48
- $LOAD_PATH.unshift(dir)
49
- Dir[File.join(dir, "*.rb")].each { |file| load File.basename(file) }
50
- end
51
-
52
- def load_gem_recipes
53
- # TODO: I'm sure there is a better way to do this...
54
- gems = open('Gemfile').grep(/blazing-/).map { |l| l.match(/(blazing-.*)\'\,/)[1] }
55
- gems.each do |gem|
56
- gem_lib_path = $:.find { |p| p.include? gem }
57
- recipes_path = File.join(gem_lib_path, gem, 'recipes')
58
- recipes = Dir.entries(recipes_path).delete_if { |r| r == '.' || r == '..' }
59
- recipes.each { |recipe| require File.join(gem, 'recipes', recipe) }
60
- end
61
- end
62
-
63
- def load_local_recipes
64
- #TODO: Implement
65
- end
66
-
67
- #
68
- # Return the list of available recipes based
69
- # on class hierarchy
70
- #
71
- def list
72
- descendants = []
73
-
74
- load_builtin_recipes
75
- load_gem_recipes
76
-
77
- ObjectSpace.each_object(Class) do |k|
78
- descendants.unshift k if k < self
79
- end
80
- descendants.uniq!
81
- descendants
82
- end
83
-
84
- end
85
11
  end
12
+
86
13
  end
@@ -1,7 +1,49 @@
1
+ require 'erb'
2
+ require 'methadone'
3
+
1
4
  class Blazing::Runner
2
5
 
3
- def run(command)
4
- system command
6
+ include Methadone::CLILogging
7
+
8
+ def initialize(config = nil, target = nil)
9
+ if config
10
+ @config = config
11
+ @target = @config.targets.find { |t| t.name == target.to_s } || @config.default_target
12
+ end
13
+ end
14
+
15
+ def exec(command)
16
+ command_method = "#{command.gsub(':', '_')}_command"
17
+ raise "Unknown Command: #{command}" unless self.respond_to? command_method
18
+ self.send command_method
19
+ end
20
+
21
+ def init_command
22
+ info("Creating an example config file in #{Blazing::DEFAULT_CONFIG_LOCATION}")
23
+ info("Customize it to your needs")
24
+
25
+ Dir.mkdir 'config' unless Dir.exists? 'config'
26
+ configuration_file = ERB.new(File.read("#{Blazing::TEMPLATE_ROOT}/config.erb")).result
27
+
28
+ File.open(Blazing::DEFAULT_CONFIG_LOCATION,"wb") do |f|
29
+ f.puts configuration_file
30
+ end
31
+ end
32
+
33
+ def setup_local_command
34
+ repository = Grit::Repo.new(Dir.pwd)
35
+ @config.targets.each do |target|
36
+ info("Adding new remote #{target.name} pointing to #{target.location}")
37
+ repository.config["remote.#{target.name}.url"] = target.location
38
+ end
39
+ end
40
+
41
+ def setup_remote_command
42
+ @config.default_target.setup
43
+ end
44
+
45
+ def update_command
46
+ @config.default_target.update
5
47
  end
6
48
 
7
49
  end
@@ -0,0 +1,7 @@
1
+ class Blazing::Shell
2
+
3
+ def run(command)
4
+ system command
5
+ end
6
+
7
+ end
@@ -1,151 +1,61 @@
1
- require 'blazing'
2
- require 'blazing/core_ext/object'
3
- require 'blazing/config'
4
- require 'blazing/bootstrap'
1
+ require 'blazing/shell'
5
2
 
6
- module Blazing
7
- class Target
3
+ class Blazing::Target
8
4
 
9
- include Blazing::Target::Bootstrap
5
+ attr_accessor :name, :location, :options
10
6
 
11
- attr_accessor :name, :recipes
12
-
13
- AVAILABLE_SETTINGS = [:deploy_to, :host, :user, :path, :default, :branch]
14
-
15
- class << self
16
-
17
- def bootstrap(name)
18
- target = config.find_target(name)
19
- runner = Blazing::Runner.new
20
- # TODO: Use a Wrapper to Net::SSH
21
- target.clone_repository
22
- target.setup_post_receive_hook
23
- setup target.name
24
- end
25
-
26
- def deploy(name)
27
- target = config.find_target(name)
28
- runner = Blazing::Runner.new
29
- deploy_command = "git push #{name}"
30
- deploy_command += " #{target.branch}:#{target.branch}" if target.branch
31
-
32
- # TODO: checkout branch if we pushed to a branch which is not checked out
33
- runner.run deploy_command
34
- end
35
-
36
- def setup(name)
37
- if name
38
- config.find_target(name).add_target_as_remote
39
- else
40
- config.targets.each do |target|
41
- target.add_target_as_remote
42
- end
43
- end
44
- end
45
-
46
- def post_receive(name)
47
- target = config.find_target(name)
48
- target.set_git_dir
49
- target.reset_head!
50
- @recipes.delete_if { |recipe| recipe.name == 'rvm' }
51
- target.run_recipes
52
- end
53
-
54
- def config
55
- Blazing::Config.parse
56
- end
57
-
58
- def runner
59
- Blazing::Runner.new
60
- end
61
-
62
- end
63
-
64
- def initialize(name, options = {})
65
- @name = name.to_s
66
- @logger = options[:_logger] ||= Blazing::Logger.new
67
- @runner = options[:_runner] ||= Blazing::Runner.new
68
- @hook = options[:_hook] ||= Blazing::CLI::Hook
69
-
70
- @config = options[:config] || Blazing::Config.parse
71
-
72
- @repository = @config.repository
73
- create_accessors_from_config(options)
74
- load_recipes
75
- end
76
-
77
- def create_accessors_from_config(options)
78
- assign_settings(options)
79
- parse_deploy_to_string unless @deploy_to.blank?
80
- ensure_mandatory_settings
81
- end
7
+ def initialize(name, location, config, options = {})
8
+ @name = name
9
+ @location = location
10
+ @config = config
11
+ @options = options
12
+ @shell = Blazing::Shell.new
13
+ end
82
14
 
83
- def assign_settings(options)
84
- AVAILABLE_SETTINGS.each do |option|
85
- instance_variable_set("@#{option}", options[option])
86
- self.class.send(:attr_accessor, option)
87
- end
88
- end
15
+ def setup
16
+ @shell.run "ssh #{user}@#{host} '#{clone_repository} && #{setup_repository}'"
17
+ self.update
18
+ end
89
19
 
90
- # If the :deploy_to option is set, user, host and path are overriden
91
- def parse_deploy_to_string
92
- @host = @deploy_to.scan(/@(.*):/).join
93
- @user = @deploy_to.scan(/(.*)@/).join
94
- @path = @deploy_to.scan(/:(.*)/).join
95
- end
20
+ def update
21
+ hook = ERB.new(File.read("#{Blazing::TEMPLATE_ROOT}/hook.erb")).result(binding)
96
22
 
97
- # Raise an error if one of the required options is still empty
98
- def ensure_mandatory_settings
99
- [:host, :user, :path].each do |option|
100
- raise "#{option} can't be blank!" if instance_variable_get("@#{option}").blank?
101
- end
23
+ File.open(Blazing::TMP_HOOK, "wb") do |f|
24
+ f.puts hook
102
25
  end
103
26
 
104
- def set_git_dir
105
- ENV['GIT_DIR'] = '.git'
106
- end
27
+ copy_hook
28
+ @shell.run "ssh #{user}@#{host} #{make_hook_executable}"
29
+ end
107
30
 
108
- def reset_head!
109
- @runner ||= Blazing::Runner.new
110
- @runner.run 'git reset --hard HEAD'
111
- end
31
+ def path
32
+ @location.match(/:(.*)$/)[1]
33
+ end
112
34
 
113
- #
114
- # Called by post-receive hook to determine rvm usage
115
- #
116
- def use_rvm?
117
- @rvm_recipe = @recipes.find { |recipe| recipe.name == 'rvm' }
118
- @recipes.delete_if { |recipe| recipe.name == 'rvm' }
119
- if @rvm_recipe
120
- @rvm_recipe.options[:rvm_string]
121
- else
122
- 'none'
123
- end
124
- end
35
+ def host
36
+ host = @location.match(/@(.*):/)
37
+ host[1] unless host.nil?
38
+ end
125
39
 
126
- def load_recipes
40
+ def user
41
+ user = @location.match(/(.*)@/)
42
+ user[1] unless user.nil?
43
+ end
127
44
 
128
- # TODO: For now, recipes can be assigned only in the global
129
- # namespace of the config. Make it possible for targets to
130
- # define recipes individually
45
+ def clone_repository
46
+ "git clone #{@config.repository} #{path}"
47
+ end
131
48
 
132
- @recipes = @config.recipes if @recipes.blank?
133
- Blazing::Recipe.load_builtin_recipes
134
- end
49
+ def copy_hook
50
+ @shell.run "scp #{Blazing::TMP_HOOK} #{user}@#{host}:#{path}/.git/hooks/post-receive"
51
+ end
135
52
 
136
- def run_recipes
137
- run_bootstrap_recipes
138
- @recipes.each do |recipe|
139
- recipe.run
140
- end
141
- end
53
+ def make_hook_executable
54
+ "chmod +x #{path}/.git/hooks/post-receive"
55
+ end
142
56
 
143
- def run_bootstrap_recipes
144
- bundler = @recipes.find { |r| r.name == 'bundler' }
145
- if bundler
146
- bundler.run
147
- @recipes.delete_if { |r| r.name == 'bundler' }
148
- end
149
- end
57
+ def setup_repository
58
+ "cd #{path} && git config receive.denyCurrentBranch ignore"
150
59
  end
60
+
151
61
  end