git-runner 0.0.6 → 0.1.0

Sign up to get free protection for your applications and to get access to all the features.
data/README.md CHANGED
@@ -1,39 +1,124 @@
1
1
  # Git Runner
2
2
 
3
- git-runner 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.
3
+ [![Code Climate](https://codeclimate.com/badge.png)](https://codeclimate.com/github/JamesBrooks/git-runner)
4
4
 
5
- [Demonstration Video, Using git-runner to perform automatic deploys](http://ascii.io/a/1349)
6
5
 
7
- Configuration for git-runner 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
+ Git Runner is a ruby framework to create and run tasks after commits are pushed to a git repository.
7
+
8
+ It works by having the git server-side post-update hook (`hooks/post-update`) invoke `git-runner`. Git Runner inspects all of the pushed branches (`refs/heads/*`) and looks for any Git Runner tasks (called instructions) to run. Such an instruction is `Deploy`, which allows for the application to be deployed after a push.
9
+
10
+ Git Runner instructions are distrubuted as separate gems, see **Available Instructions** below for what instructions are currently available for use.
11
+
12
+ Have a look at this short [demonistration video](http://ascii.io/a/1349) for what it looks like for a user pushing to a Git Runner enabled repository (using `git-runner-deploy`).
13
+
14
+
15
+ ## Why?
16
+
17
+ #### Origins
18
+
19
+ Git Runner originated from a need to simply the deployment process (capistrano) within my organisation for people who don't have their environments setup with ruby and the required gems. I wanted to make the process work for even those who are using GUI git clients.
20
+
21
+
22
+ #### Wouldn't a simple script do the job?
23
+
24
+ While you certainly could create a ruby script at `hooks/post-update` and have that run after a push with good results, Git Runner provides a robust framework for authoring re-usable tasks that you can share between all of your code repositories and run only when you need to run them.
25
+
26
+ You could do it all in a script of your own if you like, but Git Runner is there to help you out and make the process less error-prone and more maintainable.
27
+
28
+
29
+ ## How
30
+
31
+ After pushing to a git repository various server-side hooks are ran, including a hook named `post-update`. `post-update` needs to run `git-runner` with it's arguments.
32
+
33
+ Git Runner looks for pushes to any branches (`refs/heads/*`). For each branch it greps the repository for one or more Git Runner instructions within a instruction file.
34
+
35
+ The instruction file and instruction markers are configurable (see **Configuration** below), by default the configuration file is `config/deploy.rb` and Git Runner instructions begin with `# GitRunner:`, e.g.
36
+
37
+ ```
38
+ # GitRunner: Deploy
39
+ ... rest of the file ...
40
+ ```
41
+
42
+ *Instructions can be on any line within the instruction file and multiple instructions are present (instructions are ran in order).*
43
+
44
+ Any instructions that are found within the instruction file are then processed and ran if able, with any messages being displayed to the user currently pushing the repository.
45
+
46
+ Any errors encountered will also be displayed with all relevent details being written to a log on the server.
47
+
48
+
49
+ ## Features
50
+
51
+ * Robust framework for running tasks (instructions) after updates are made to a repository.
52
+ * Have all of your tasks available to all of your repositories, only use the ones you need on a per repository basis.
53
+ * Activiation through instructions contained within the repository.
54
+ * Branch detection, behave differently for different branches if you like.
55
+ * Simple authoring of additional instructions.
56
+ * Error handling, notification and logging.
57
+
58
+
59
+ ## Available Instructions
60
+
61
+ Name | Gem | Description
62
+ ---------------------------------------------------------- | ----------------- | ---------------------------------------
63
+ [Deploy](https://github.com/JamesBrooks/git-runner-deploy) | git-runner-deploy | Application deployment using capistrano
8
64
 
9
- Instructions are contained within `lib/git-runner/instructions`. By default the instructions contained here and only used for support purposes, real functionality is abstracted into separate gems (at this time the only real instuction is deploy).
10
65
 
11
66
  ## Installation
12
67
 
13
- $ gem install git-runner
68
+ This section needs a lot more fleshing out as installation overly trivial. The main thing to worry about is making sure that `hooks/post-update` in each of your repositories is able to run `git-runner` (and pass it's arguments on).
69
+
70
+
71
+ #### Install the git-runner
14
72
 
15
- ## Install any additional modules (you'll want to do this to get any real functionality)
73
+ Install the gem and any other additional instruction gems that you need, for example if we want Git Runner with capistrano deploy support:
16
74
 
17
- ### Deploy using Capistrano
18
- [https://github.com/JamesBrooks/git-runner-deploy](https://github.com/JamesBrooks/git-runner-deploy)
75
+ ```
76
+ gem install git-runner
77
+ gem install git-runner-deploy
78
+ ```
19
79
 
20
- gem install git-runner-deploy
80
+ #### Hook up git-runner to fire on hooks/post-update
21
81
 
82
+ There are a few ways to accomplish this, such as directly symlinking `hooks/post-update` to the executable path for git-runner. Currently I prefer the following approach of creating a script that loads rubygems and git-runner + calls git runner. The following is the contents of my `hooks/post-update` file:
22
83
 
23
- ## Usage
84
+ ```
85
+ #!/usr/bin/env ruby
86
+
87
+ require 'rubygems'
88
+ require 'git-runner'
89
+
90
+ GitRunner::Base.new(ARGV).run
91
+ ```
24
92
 
25
- 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.
26
93
 
27
94
  ## Configuration
28
95
 
29
- Configuration can be overwritten through a YAML file at either `/etc/git-runner.yml` or `$HOME/.git-runner.yml`. The current configuration options are:
96
+ Configuration can be changed through a YAML file at either `/etc/git-runner.yml` or `$HOME/.git-runner.yml`. The default configuration settings are:
97
+
98
+ Option | Default Value | Description
99
+ ------------------ | ---------------- | --------------------------------------------------------------------------
100
+ git_executable | /usr/bin/env git | Location of the git executable to use
101
+ instruction_file | config/deploy.rb | Where to look for Git Runner instructions within each repository
102
+ instruction_prefix | # GitRunner: | What Git Runner instructions will be prefixed with in the instruction file
103
+ tmp_directory | /tmp/git-runner | Working directory for git-runner
104
+
105
+
106
+ ## TODOs
107
+
108
+ * Support to monitor a command (stdout and stderr) as the command is running, not just at the end.
109
+ * Instruction file path is globally set, make this overwritable on a per-repository basis?
110
+ * Instruction prefix is globally set, make this overwritable on a per-repository basis?
111
+ * Have core functionality fire useful hooks.
112
+ * Improve the output/text library, it can work better!
113
+ * Simplify Base#run (CodeClimate)
114
+
115
+
116
+ ## Support
117
+
118
+ Check out [GitHub Issues](https://github.com/JamesBrooks/git-runner/issues) to see if anyone else has had the same problem you have, or create a new issue.
30
119
 
31
- * **git_executable** (default: '/usr/bin/env git')
32
- * **instruction_file** (default: 'config/deploy.rb')
33
- * **instruction_prefix** (default: '# GitRunner:')
34
- * **tmp_directory** (default: '/tmp/git-runner')
120
+ Also feel free to contact me directly (james at jamesbrooks dot net) for non-support comments or support of a more sensitive nature.
35
121
 
36
- ## TODO
37
122
 
38
123
  ## Contributing
39
124
 
@@ -16,5 +16,5 @@ Gem::Specification.new do |gem|
16
16
  gem.executables = gem.files.grep(%r{^bin/}).map{ |f| File.basename(f) }
17
17
  gem.require_paths = ["lib"]
18
18
 
19
- gem.add_dependency 'session'
19
+ gem.add_dependency 'session', '>= 3.1.0'
20
20
  end
@@ -26,31 +26,13 @@ module GitRunner
26
26
 
27
27
 
28
28
  rescue GitRunner::Instruction::Failure => ex
29
- Text.new_line
30
- Text.out("Stopping runner, no further instructions will be performed\n")
31
- Text.new_line
29
+ handle_instruction_failure_exception(ex)
32
30
 
33
31
  rescue GitRunner::Command::Failure => ex
34
- Text.new_line
35
- Text.out(Text.red("\u2716 Command failed: " + Text.red(ex.result.command)), :heading)
36
-
37
- write_error_log do |log|
38
- log << Command.history_to_s
39
- end
40
-
41
- Text.new_line
32
+ handle_command_failure_exception(ex)
42
33
 
43
34
  rescue Exception => ex
44
- Text.new_line
45
- Text.out(Text.red("\u2716 Unknown exception occured: #{ex}"), :heading)
46
-
47
- write_error_log do |log|
48
- log << Command.history_to_s
49
- log << ex.message + "\n"
50
- log << ex.backtrace.map { |line| " #{line}\n" }.join
51
- end
52
-
53
- Text.new_line
35
+ handle_unknown_exception(ex)
54
36
 
55
37
  ensure
56
38
  Text.finish
@@ -64,6 +46,36 @@ module GitRunner
64
46
  Gem::Specification._all.map(&:name).select { |gem| gem =~ /^git-runner-/ }.each { |name| require(name) }
65
47
  end
66
48
 
49
+ def handle_instruction_failure_exception(ex)
50
+ Text.new_line
51
+ Text.out("Stopping runner, no further instructions will be performed\n")
52
+ Text.new_line
53
+ end
54
+
55
+ def handle_command_failure_exception(ex)
56
+ Text.new_line
57
+ Text.out(Text.red("\u2716 Command failed: " + Text.red(ex.result.command)), :heading)
58
+
59
+ write_error_log do |log|
60
+ log << Command.history_to_s
61
+ end
62
+
63
+ Text.new_line
64
+ end
65
+
66
+ def handle_unknown_exception(ex)
67
+ Text.new_line
68
+ Text.out(Text.red("\u2716 Unknown exception occured: #{ex}"), :heading)
69
+
70
+ write_error_log do |log|
71
+ log << Command.history_to_s
72
+ log << ex.message + "\n"
73
+ log << ex.backtrace.map { |line| " #{line}\n" }.join
74
+ end
75
+
76
+ Text.new_line
77
+ end
78
+
67
79
  def write_error_log
68
80
  log_directory = File.join(Configuration.tmp_directory, 'logs')
69
81
  error_log = File.join(log_directory, Time.now.strftime("%Y%m%d%H%M%S") + '-error.log')
@@ -32,10 +32,9 @@ module GitRunner
32
32
 
33
33
  # Process the output to generate instructions
34
34
  output.split("\n").map do |line|
35
- instruction = Instruction.from_raw(line)
36
- instruction.branch = self
37
-
38
- instruction
35
+ Instruction.from_raw(line).tap do |instruction|
36
+ instruction.branch = self
37
+ end
39
38
  end
40
39
  end
41
40
  end
@@ -6,17 +6,21 @@ module GitRunner
6
6
 
7
7
 
8
8
  def execute(*commands)
9
- commands.each do |command|
10
- out = StringIO::new
9
+ # Extract options (if any)
10
+ opts = commands.last.is_a?(Hash) ? commands.pop : {}
11
11
 
12
- session.execute command, :stdout => out, :stderr => out
12
+ # Set session callback procs
13
+ session.outproc = opts[:outproc]
14
+ session.errproc = opts[:errproc]
13
15
 
14
- result = Result.new(command, out.string, session.exit_status)
15
- history << result
16
+ # Cycle through and run commands
17
+ execute_commands(commands)
16
18
 
17
- raise Failure.new(result) if result.failure?
18
- end
19
+ # Clear session callback procs
20
+ session.outproc = nil
21
+ session.errproc = nil
19
22
 
23
+ # Return full output of the last ran command (should this be all commands perhaps?)
20
24
  history.last.out
21
25
  end
22
26
 
@@ -25,13 +29,27 @@ module GitRunner
25
29
  end
26
30
 
27
31
  def history_to_s
28
- history.map do |result|
29
- "Status: #{result.status}; Command: #{result.command}\n#{result.out}\n--------------------\n"
30
- end.join
32
+ history.map(&:to_s).join("\n#{'-' * 20}\n")
31
33
  end
32
34
 
33
35
 
34
36
  private
37
+ def execute_commands(commands)
38
+ commands.each do |command|
39
+ out = StringIO::new
40
+
41
+ # Run the command through the active shell session
42
+ session.execute(command, :stdout => out, :stderr => out)
43
+
44
+ # Construct result and store is as part of the command history
45
+ result = Result.new(command, out.string, session.exit_status)
46
+ history << result
47
+
48
+ # Bubble up a failure exception if the command failed
49
+ raise Failure.new(result) if result.failure?
50
+ end
51
+ end
52
+
35
53
  def session
36
54
  @session ||= Session::Bash::Login.new
37
55
  end
@@ -57,8 +75,13 @@ module GitRunner
57
75
  def failure?
58
76
  !success?
59
77
  end
78
+
79
+ def to_s
80
+ "Status: #{status}; Command: #{command}\n#{out}"
81
+ end
60
82
  end
61
83
 
84
+
62
85
  class Failure < StandardError
63
86
  attr_accessor :result
64
87
 
@@ -1,5 +1,7 @@
1
1
  module GitRunner
2
- class Text
2
+ module Text
3
+ extend self
4
+
3
5
  COLOR_START = "\033["
4
6
  COLOR_END = "m"
5
7
  COLOR_RESET = "#{COLOR_START}0#{COLOR_END}"
@@ -22,45 +24,53 @@ module GitRunner
22
24
  }
23
25
 
24
26
 
25
- class << self
26
- def begin
27
- STDOUT.sync = true
28
- print("\e[1G \e[1G")
29
- end
27
+ def begin
28
+ STDOUT.sync = true
29
+ print("\e[1G \e[1G")
30
+ end
30
31
 
31
- def finish
32
- print("\n")
33
- end
32
+ def finish
33
+ print("\n")
34
+ end
34
35
 
35
- def out(str, style=nil)
36
- # Ensure that new lines overwrite the default 'remote: ' text
37
- str = str.gsub("\n", "\n\e[1G#{padding(nil)}")
36
+ def out(str, style=nil)
37
+ # Ensure that new lines overwrite the default 'remote: ' text
38
+ str = str.gsub("\n", "\n\e[1G#{padding(nil)}")
38
39
 
39
- print "\n\e[1G#{padding(style).ljust(7)}#{str}"
40
- end
40
+ print "\n\e[1G#{padding(style).ljust(7 + (@indentation || 0))}#{str}"
41
+ end
41
42
 
42
- def new_line
43
- out('')
44
- end
43
+ def new_line
44
+ out('')
45
+ end
45
46
 
46
- # Color methods
47
- COLORS.each do |color, code|
48
- class_eval <<-EOF
49
- def #{color}(str, style=:normal)
50
- "#{COLOR_START}\#{STYLES[style]};#{code}#{COLOR_END}\#{str}#{COLOR_RESET}"
51
- end
52
- EOF
53
- end
47
+ def indent(spaces=2)
48
+ @indentation ||= 0
49
+ @indentation += spaces
54
50
 
51
+ yield
52
+
53
+ @indentation -= spaces
54
+ Text.new_line
55
+ end
55
56
 
56
- private
57
- def padding(style)
58
- case style
59
- when :heading
60
- '----->'
61
- else
62
- ''
57
+ # Color methods
58
+ COLORS.each do |color, code|
59
+ class_eval <<-EOF
60
+ def #{color}(str, style=:normal)
61
+ "#{COLOR_START}\#{STYLES[style]};#{code}#{COLOR_END}\#{str}#{COLOR_RESET}"
63
62
  end
63
+ EOF
64
+ end
65
+
66
+
67
+ private
68
+ def padding(style)
69
+ case style
70
+ when :heading
71
+ '----->'
72
+ else
73
+ ''
64
74
  end
65
75
  end
66
76
  end
@@ -1,3 +1,3 @@
1
1
  module GitRunner
2
- VERSION = '0.0.6'
2
+ VERSION = '0.1.0'
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.6
4
+ version: 0.1.0
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-09 00:00:00.000000000 Z
12
+ date: 2012-10-12 00:00:00.000000000 Z
13
13
  dependencies:
14
14
  - !ruby/object:Gem::Dependency
15
15
  name: session
@@ -18,7 +18,7 @@ dependencies:
18
18
  requirements:
19
19
  - - ! '>='
20
20
  - !ruby/object:Gem::Version
21
- version: '0'
21
+ version: 3.1.0
22
22
  type: :runtime
23
23
  prerelease: false
24
24
  version_requirements: !ruby/object:Gem::Requirement
@@ -26,7 +26,7 @@ dependencies:
26
26
  requirements:
27
27
  - - ! '>='
28
28
  - !ruby/object:Gem::Version
29
- version: '0'
29
+ version: 3.1.0
30
30
  description: GitRunner is a ruby framework to implement and run tasks after code has
31
31
  been pushed to a Git repository. It works by invoking `git-runner` through `hooks/post-update`
32
32
  in your remote Git repository.