git-runner 0.0.2 → 0.0.3

Sign up to get free protection for your applications and to get access to all the features.
data/README.md CHANGED
@@ -2,6 +2,8 @@
2
2
 
3
3
  GitRunner is a ruby framework to implement and run tasks after code has been pushed to a Git repository. It works by invoking `git-runner` through `hooks/post-update` in your remote Git repository.
4
4
 
5
+ [Demonstration Video, Using git-runner to perform automatic deploys](http://ascii.io/a/1349)
6
+
5
7
  Configuration for GitRunner is read from a pre-determined file within the repository (at this time, that file is config/deploy.rb but this will soon be configurable). Any instructions detected are then processed and ran.
6
8
 
7
9
  Instructions are contained within `lib/git-runner/instructions`, though soon these will be extracted out to separate gems, leaving only the core instructions present. Currently the only *real* instruction that users will want to run is Deploy, which will checkout your code, run bundler and perform a cap deploy (with multistage support).
@@ -14,10 +16,19 @@ Instructions are contained within `lib/git-runner/instructions`, though soon the
14
16
 
15
17
  Symlink `hooks/post-update` to `git-runner`, or if `post-update` is already in use modify it to run `git-runner` with the arguments supplied to the hook.
16
18
 
19
+ ## Configuration
20
+
21
+ Configuration can be overwritten through a YAML file at either `/etc/git-runner.yml` or `$HOME/.git-runner.yml`. The current configuration options are:
22
+
23
+ * **git_executable** (default: '/usr/bin/env git')
24
+ * **instruction_file** (default: 'config/deploy.rb')
25
+ * **instruction_prefix** (default: '# GitRunner:')
26
+ * **tmp_directory** (default: '/tmp/git-runner')
27
+
17
28
  ## TODO
18
29
 
19
- * Allow file based configuration (currently configuration is hard-coded)
20
30
  * Extract our non-core instruction functionality into individual gems (e.g. the Deploy instruction)
31
+ * Format README.md nicely
21
32
 
22
33
  ## Contributing
23
34
 
data/git-runner.gemspec CHANGED
@@ -1,7 +1,11 @@
1
1
  # -*- encoding: utf-8 -*-
2
- require File.expand_path('../lib/git-runner/version', __FILE__)
2
+ lib = File.expand_path('../lib', __FILE__)
3
+ $LOAD_PATH.unshift(lib) unless $LOAD_PATH.include?(lib)
4
+ require 'git-runner/version'
3
5
 
4
6
  Gem::Specification.new do |gem|
7
+ gem.name = "git-runner"
8
+ gem.version = GitRunner::VERSION
5
9
  gem.authors = ["James Brooks"]
6
10
  gem.email = ["james@jamesbrooks.net"]
7
11
  gem.summary = "Ruby framework to run tasks after code has been pushed to a Git repository."
@@ -10,9 +14,7 @@ Gem::Specification.new do |gem|
10
14
 
11
15
  gem.files = `git ls-files`.split($\)
12
16
  gem.executables = gem.files.grep(%r{^bin/}).map{ |f| File.basename(f) }
13
- gem.name = "git-runner"
14
17
  gem.require_paths = ["lib"]
15
- gem.version = GitRunner::VERSION
16
18
 
17
19
  gem.add_dependency 'session'
18
20
  end
data/lib/git-runner.rb CHANGED
@@ -1,17 +1,11 @@
1
- require "git-runner/version"
2
1
  require "git-runner/base"
2
+ require "git-runner/branch"
3
3
  require "git-runner/command"
4
4
  require "git-runner/configuration"
5
- require "git-runner/branch"
5
+ require "git-runner/hooks"
6
6
  require "git-runner/instruction"
7
7
  require "git-runner/text"
8
+ require "git-runner/version"
8
9
 
9
10
  module GitRunner
10
- DEFAULT_CONFIGURATION = {
11
- :git_executable => '/usr/bin/env git',
12
- :instruction_file => 'config/deploy.rb',
13
- :instruction_prefix => '# GitRunner:',
14
- :tmp_directory => '/tmp/git-runner',
15
- :rvm_ruby_version => '1.9.3'
16
- }
17
11
  end
@@ -24,38 +24,50 @@ module GitRunner
24
24
  end
25
25
 
26
26
 
27
- rescue GitRunner::Instruction::InstructionHalted => ex
27
+ rescue GitRunner::Instruction::Failure => ex
28
+ Text.new_line
28
29
  Text.out("Stopping runner, no further instructions will be performed\n")
30
+ Text.new_line
29
31
 
30
32
  rescue GitRunner::Command::Failure => ex
31
- Text.out("\n")
33
+ Text.new_line
32
34
  Text.out(Text.red("\u2716 Command failed: " + Text.red(ex.result.command)), :heading)
33
35
 
34
- # Write error log
35
- log_directory = File.join(Configuration.tmp_directory, 'logs')
36
- error_log = File.join(log_directory, Time.now.strftime("%Y%m%d%H%M%S") + '-error.log')
37
-
38
- Dir.mkdir(log_directory) unless Dir.exists?(log_directory)
39
- File.open(error_log, 'w') do |file|
40
- Command.history.each do |result|
41
- file << "Status: #{result.status}; Command: #{result.command}\n"
42
- file << result.out
43
- file << "\n--------------------\n"
44
- end
36
+ write_error_log do |log|
37
+ log << Command.history_to_s
45
38
  end
46
39
 
47
- Text.out("An error log has been created: #{error_log}")
48
40
  Text.new_line
49
41
 
50
42
  rescue Exception => ex
51
43
  Text.new_line
52
44
  Text.out(Text.red("\u2716 Unknown exception occured: #{ex}"), :heading)
53
- Text.out(ex.backtrace.join("\n").gsub("\n", "\n#{''.ljust(10)}"))
45
+
46
+ write_error_log do |log|
47
+ log << Command.history_to_s
48
+ log << ex.message + "\n"
49
+ log << ex.backtrace.map { |line| " #{line}\n" }.join
50
+ end
51
+
54
52
  Text.new_line
55
53
 
56
54
  ensure
57
55
  Text.finish
58
56
  end
59
57
  end
58
+
59
+
60
+ private
61
+ def write_error_log
62
+ log_directory = File.join(Configuration.tmp_directory, 'logs')
63
+ error_log = File.join(log_directory, Time.now.strftime("%Y%m%d%H%M%S") + '-error.log')
64
+
65
+ Command.execute("mkdir -p #{log_directory}") unless Dir.exists?(log_directory)
66
+
67
+ File.open(error_log, 'w') { |file| yield(file) }
68
+
69
+ Text.out("An error log has been created: #{error_log}")
70
+ error_log
71
+ end
60
72
  end
61
73
  end
@@ -24,6 +24,12 @@ module GitRunner
24
24
  @history ||= []
25
25
  end
26
26
 
27
+ def history_to_s
28
+ history.map do |result|
29
+ "Status: #{result.status}; Command: #{result.command}\n#{result.out}\n--------------------\n"
30
+ end.join
31
+ end
32
+
27
33
 
28
34
  private
29
35
  def session
@@ -2,19 +2,45 @@ module GitRunner
2
2
  module Configuration
3
3
  extend self
4
4
 
5
+ FILE_LOCATIONS = %w(/etc/git-runner.yml ~/.git-runner.yml)
6
+
7
+ DEFAULTS = {
8
+ 'git_executable' => '/usr/bin/env git',
9
+ 'instruction_file' => 'config/deploy.rb',
10
+ 'instruction_prefix' => '# GitRunner:',
11
+ 'tmp_directory' => '/tmp/git-runner',
12
+ }
13
+
14
+
5
15
  def __attributes
6
- # TODO: Load configuration from /etc/git-runner.conf
7
- @attributes ||= DEFAULT_CONFIGURATION.dup
16
+ @attributes ||= __load_attributes
8
17
  end
9
18
 
10
19
  def method_missing(method, *args, &block)
11
- if __attributes.keys.include?(method)
12
- __attributes[method]
20
+ method_s = method.to_s
21
+
22
+ if __attributes.keys.include?(method_s)
23
+ __attributes[method_s]
13
24
  elsif method.to_s.end_with?('=')
14
- __attributes[method.to_s[0..-2].to_sym] = args[0]
25
+ __attributes[method_s[0..-2]] = args[0]
15
26
  else
16
27
  super
17
28
  end
18
29
  end
30
+
31
+
32
+ private
33
+ def __load_attributes
34
+ attrs = DEFAULTS.dup
35
+
36
+ FILE_LOCATIONS.each do |location|
37
+ begin
38
+ path = File.expand_path(location)
39
+ attrs.merge!(YAML.load_file(path)) if File.exist?(path)
40
+ end
41
+ end
42
+
43
+ attrs
44
+ end
19
45
  end
20
46
  end
@@ -0,0 +1,19 @@
1
+ module GitRunner
2
+ module Hooks
3
+ extend self
4
+
5
+
6
+ def registrations
7
+ @registrations ||= Hash.new { |hash, key| hash[key] = [] }
8
+ end
9
+
10
+ def register(name, object, method)
11
+ registrations[name] << [ object, method ]
12
+ end
13
+
14
+ def fire(name)
15
+ return unless registrations.keys.include?(name)
16
+ registrations[name].each { |object, method| object.send(method) }
17
+ end
18
+ end
19
+ end
@@ -2,22 +2,16 @@ Dir[File.join(File.dirname(__FILE__), 'instructions', '*.rb')].each { |file| req
2
2
 
3
3
  module GitRunner
4
4
  class Instruction
5
- class InstructionHalted < StandardError ; end
6
-
7
-
8
5
  def self.new(name, args={})
9
6
  begin
10
7
  const_get(name).new(args)
11
8
 
12
9
  rescue NameError => e
13
- # Return a console message instruction to inform the user
14
- instruction = ConsoleMessage.new(Text.red("\u2716 Instruction not found: #{name}"))
15
- instruction.opts = {
16
- :halt => true,
10
+ # Display message for missing instruction, halt further execution
11
+ Display.new(Text.red("\u2716 Instruction not found: #{name}"), {
12
+ :fail => true,
17
13
  :priority => true
18
- }
19
-
20
- instruction
14
+ })
21
15
  end
22
16
  end
23
17
 
@@ -26,3 +20,10 @@ module GitRunner
26
20
  end
27
21
  end
28
22
  end
23
+
24
+ module GitRunner
25
+ class Instruction
26
+ class Failure < StandardError
27
+ end
28
+ end
29
+ end
@@ -4,8 +4,9 @@ module GitRunner
4
4
  attr_accessor :branch, :args, :opts
5
5
 
6
6
 
7
- def initialize(args)
7
+ def initialize(args, opts={})
8
8
  self.args = args
9
+ self.opts = opts
9
10
  end
10
11
 
11
12
  def perform
@@ -17,8 +18,8 @@ module GitRunner
17
18
  perform
18
19
  Text.new_line
19
20
 
20
- if halt?
21
- raise InstructionHalted.new(self)
21
+ if opts[:fail]
22
+ raise Failure.new(self)
22
23
  end
23
24
  end
24
25
  end
@@ -27,17 +28,8 @@ module GitRunner
27
28
  true
28
29
  end
29
30
 
30
- def halt!
31
- self.opts ||= {}
32
- self.opts[:halt] = true
33
- end
34
-
35
- def halt?
36
- self.opts && !!self.opts[:halt]
37
- end
38
-
39
- def priority?
40
- self.opts && !!self.opts[:priority]
31
+ def fail!
32
+ raise Failure.new(self)
41
33
  end
42
34
 
43
35
 
@@ -1,6 +1,6 @@
1
1
  module GitRunner
2
2
  class Instruction
3
- class ConsoleMessage < Base
3
+ class Display < Base
4
4
  def perform
5
5
  Text.out(message, :heading)
6
6
  end
@@ -1,3 +1,3 @@
1
1
  module GitRunner
2
- VERSION = '0.0.2'
2
+ VERSION = '0.0.3'
3
3
  end
metadata CHANGED
@@ -1,7 +1,7 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: git-runner
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.0.2
4
+ version: 0.0.3
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-07 00:00:00.000000000 Z
13
13
  dependencies:
14
14
  - !ruby/object:Gem::Dependency
15
15
  name: session
@@ -49,10 +49,10 @@ files:
49
49
  - lib/git-runner/branch.rb
50
50
  - lib/git-runner/command.rb
51
51
  - lib/git-runner/configuration.rb
52
+ - lib/git-runner/hooks.rb
52
53
  - lib/git-runner/instruction.rb
53
54
  - lib/git-runner/instructions/base.rb
54
- - lib/git-runner/instructions/console_message.rb
55
- - lib/git-runner/instructions/deploy.rb
55
+ - lib/git-runner/instructions/display.rb
56
56
  - lib/git-runner/text.rb
57
57
  - lib/git-runner/version.rb
58
58
  homepage: https://github.com/JamesBrooks/git-runner
@@ -1,111 +0,0 @@
1
- require 'digest'
2
-
3
- module GitRunner
4
- class Instruction
5
-
6
- # Performs deployments using capistrano (cap deploy)
7
- class Deploy < Base
8
- attr_accessor :clone_directory
9
-
10
-
11
- def should_run?
12
- branches.empty? || branches.include?(branch.name)
13
- end
14
-
15
- def perform
16
- start_time = Time.now
17
-
18
- Text.out(Text.green("Performing Deploy (#{environment_from_branch(branch)})"), :heading)
19
-
20
- checkout_branch
21
-
22
- if missing_capfile?
23
- Text.out(Text.red("Missing Capfile, unable to complete deploy."))
24
- halt! && return
25
- end
26
-
27
- prepare_deploy_environment
28
- perform_deploy
29
- cleanup_deploy_environment
30
-
31
- end_time = Time.now
32
-
33
- Text.out(Text.green("\u2714 Deploy successful, completed in #{(end_time - start_time).ceil} seconds"))
34
- end
35
-
36
- def branches
37
- args.split(/\s+/)
38
- end
39
-
40
- def rvm_string
41
- "#{Configuration.rvm_ruby_version}@git-runner-#{branch.repository_name}-#{branch.name}"
42
- end
43
-
44
- def missing_capfile?
45
- !File.exists?("#{clone_directory}/Capfile")
46
- end
47
-
48
- def uses_bundler?
49
- File.exists?("#{clone_directory}/Gemfile")
50
- end
51
-
52
- def multistage?
53
- result = execute("grep -e 'require.*capistrano.*multistage' #{clone_directory}/#{Configuration.instruction_file} || true")
54
- !result.empty?
55
- end
56
-
57
- def checkout_branch
58
- timestamp = Time.now.strftime("%Y%m%d%H%M%S")
59
- self.clone_directory = File.join(Configuration.tmp_directory, "#{branch.repository_name}-#{environment_from_branch(branch)}-#{timestamp}")
60
-
61
- Text.out("Checking out #{branch.name} to #{clone_directory}")
62
-
63
- execute(
64
- "mkdir -p #{clone_directory}",
65
- "git clone --depth=1 --branch=#{branch.name} file://#{branch.repository_path} #{clone_directory}"
66
- )
67
- end
68
-
69
- def prepare_deploy_environment
70
- Text.out("Preparing deploy environment")
71
-
72
- if uses_bundler?
73
- execute(
74
- "cd #{clone_directory}",
75
- "bundle install --path=#{File.join(Configuration.tmp_directory, '.gems')}"
76
- )
77
- end
78
- end
79
-
80
- def perform_deploy
81
- cap_deploy_command = if multistage?
82
- Text.out("Deploying application (multistage detected)")
83
- "cap #{environment_from_branch(branch)} deploy"
84
- else
85
- Text.out("Deploying application")
86
- "cap deploy"
87
- end
88
-
89
- execute(
90
- "cd #{clone_directory}",
91
- cap_deploy_command
92
- )
93
- end
94
-
95
- def cleanup_deploy_environment
96
- Text.out("Cleaning deploy environment")
97
- execute("rm -rf #{clone_directory}")
98
- end
99
-
100
-
101
- private
102
- def environment_from_branch(branch)
103
- if branch.name == 'master'
104
- 'production'
105
- else
106
- branch.name
107
- end
108
- end
109
- end
110
- end
111
- end