git-runner 0.0.2 → 0.0.3
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- 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
|