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 +12 -1
- data/git-runner.gemspec +5 -3
- data/lib/git-runner.rb +3 -9
- data/lib/git-runner/base.rb +27 -15
- data/lib/git-runner/command.rb +6 -0
- data/lib/git-runner/configuration.rb +31 -5
- data/lib/git-runner/hooks.rb +19 -0
- data/lib/git-runner/instruction.rb +11 -10
- data/lib/git-runner/instructions/base.rb +6 -14
- data/lib/git-runner/instructions/{console_message.rb → display.rb} +1 -1
- data/lib/git-runner/version.rb +1 -1
- metadata +4 -4
- data/lib/git-runner/instructions/deploy.rb +0 -111
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
|
-
|
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/
|
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
|
data/lib/git-runner/base.rb
CHANGED
@@ -24,38 +24,50 @@ module GitRunner
|
|
24
24
|
end
|
25
25
|
|
26
26
|
|
27
|
-
rescue GitRunner::Instruction::
|
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.
|
33
|
+
Text.new_line
|
32
34
|
Text.out(Text.red("\u2716 Command failed: " + Text.red(ex.result.command)), :heading)
|
33
35
|
|
34
|
-
|
35
|
-
|
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
|
-
|
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
|
data/lib/git-runner/command.rb
CHANGED
@@ -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
|
-
|
7
|
-
@attributes ||= DEFAULT_CONFIGURATION.dup
|
16
|
+
@attributes ||= __load_attributes
|
8
17
|
end
|
9
18
|
|
10
19
|
def method_missing(method, *args, &block)
|
11
|
-
|
12
|
-
|
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[
|
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
|
-
#
|
14
|
-
|
15
|
-
|
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
|
21
|
-
raise
|
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
|
31
|
-
self
|
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
|
|
data/lib/git-runner/version.rb
CHANGED
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.
|
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-
|
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/
|
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
|