hookie 1.0.0
Sign up to get free protection for your applications and to get access to all the features.
- data/.gitignore +17 -0
- data/Gemfile +4 -0
- data/LICENSE.txt +22 -0
- data/README.md +129 -0
- data/Rakefile +4 -0
- data/bin/hookie +7 -0
- data/hookie.gemspec +27 -0
- data/lib/hookie/framework.rb +100 -0
- data/lib/hookie/plugins/base_plugin.rb +40 -0
- data/lib/hookie/plugins/empty_plugin.rb +12 -0
- data/lib/hookie/plugins/hipchat_plugin.rb +88 -0
- data/lib/hookie/plugins/jenkins_plugin.rb +39 -0
- data/lib/hookie.rb +5 -0
- data/spec/base_plugin_spec.rb +43 -0
- data/spec/framework_spec.rb +66 -0
- data/spec/hipchat_plugin_spec.rb +51 -0
- data/spec/jenkins_plugin_spec.rb +48 -0
- data/spec/spec_helper.rb +7 -0
- metadata +120 -0
data/.gitignore
ADDED
data/Gemfile
ADDED
data/LICENSE.txt
ADDED
@@ -0,0 +1,22 @@
|
|
1
|
+
Copyright (c) 2013 Marcin Szczepanski
|
2
|
+
|
3
|
+
MIT License
|
4
|
+
|
5
|
+
Permission is hereby granted, free of charge, to any person obtaining
|
6
|
+
a copy of this software and associated documentation files (the
|
7
|
+
"Software"), to deal in the Software without restriction, including
|
8
|
+
without limitation the rights to use, copy, modify, merge, publish,
|
9
|
+
distribute, sublicense, and/or sell copies of the Software, and to
|
10
|
+
permit persons to whom the Software is furnished to do so, subject to
|
11
|
+
the following conditions:
|
12
|
+
|
13
|
+
The above copyright notice and this permission notice shall be
|
14
|
+
included in all copies or substantial portions of the Software.
|
15
|
+
|
16
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
|
17
|
+
EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
|
18
|
+
MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
|
19
|
+
NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
|
20
|
+
LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
|
21
|
+
OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
|
22
|
+
WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
data/README.md
ADDED
@@ -0,0 +1,129 @@
|
|
1
|
+
# Hookie
|
2
|
+
|
3
|
+
Hookie is a pluggable framework for creating git hooks using Ruby. It was originally designed for writing hooks for gitolite, but should work for any hooks, including local ones.
|
4
|
+
|
5
|
+
## Background
|
6
|
+
|
7
|
+
After we had setup [HipChat](http://hipchat.com) at our organisation I wanted to get notifications from gitolite pushes pushed to HipChat. I found the [gitolite-hipchat-notification](https://github.com/peplin/gitolite-hipchat-notification) project, and [forked it](https://github.com/marcins/gitolite-hipchat-notification) and cleaned it up a bit for my purposes. However this script wasn't very flexible in that you needed to add it individually to each repo (or suffer with a single config). Then I wanted to add Jenkins build notifications as well, so had to enhance the script to support running multiple tasks. Eventually it was all getting too crufty and so Hookie was born!
|
8
|
+
|
9
|
+
Hookie is designed so that you can have multiple actions take place as a result of a git hook (usually post-receive), these actions are built as plugins, and configured via git - the beauty of this is that for gitolite you define the hook globally and then configure which plugins run where via git config keys, which can be defined in your gitolite-admin repository. So once Hookie is setup you don't need to make changes on the server.
|
10
|
+
|
11
|
+
Hookie includes those aforementioned plugins for HipChat and Jenkins in the base build.
|
12
|
+
|
13
|
+
## Acknowledgements
|
14
|
+
|
15
|
+
Hookie is built on the base of @peplin's (gitolite-hipchat-notification)[https://github.com/peplin/gitolite-hipchat-notification] project, which was originally forked from (gitolite-campfire-notification)[https://github.com/LegionSB/gitolite-campfire-notification] - the magic of Open Source!
|
16
|
+
|
17
|
+
Hookie uses [grit](https://github.com/mojombo/grit) for accessing information in git repositories.
|
18
|
+
|
19
|
+
## Installation
|
20
|
+
|
21
|
+
Install the gem on your git server
|
22
|
+
|
23
|
+
gem install hookie
|
24
|
+
|
25
|
+
You then need to create a post-receive script in your repository's hooks directory, there are a couple of ways to do this, see below for some options. The post-receive hook will look like this:
|
26
|
+
|
27
|
+
#!/usr/bin/env ruby
|
28
|
+
require 'hookie'
|
29
|
+
|
30
|
+
# Add your plugins here
|
31
|
+
# require 'my_plugin'
|
32
|
+
|
33
|
+
Hookie::Framework.hook "post_receive"
|
34
|
+
|
35
|
+
**NOTE** the above doesn't work if your gitolite user is using RVM as the gitolite shell won't have loaded RVM. The alternative hook script looks like this:
|
36
|
+
|
37
|
+
#!/bin/bash
|
38
|
+
source ~/.rvm/scripts/rvm
|
39
|
+
ruby -rhookie -e 'Hookie::Framework.hook "post_receive"'
|
40
|
+
|
41
|
+
If you need to add your own custom plugins then add them with additional -r arguments (require)
|
42
|
+
|
43
|
+
### Installing hooks with gitolite
|
44
|
+
|
45
|
+
The idea is that you install hookie as a post_receive hook for all your repositories, but configuration determines when the hook is actually run. To install for all repositories you create the post-receive script in the .gitolite/hooks/common/ directory. Make sure the script is exectuable.
|
46
|
+
|
47
|
+
### Installing hooks manually
|
48
|
+
|
49
|
+
You can also install Hookie hooks manually, by adding them to the hooks directory within your repository. Note that the same configuration still applies. You should also be able to install Hookie for local repository hooks, but I haven't tried this yet! Currently Hookie assumes it will be receiving changeset information on STDIN in the format gitolite provides it, I'm not sure if other git servers and/or local hooks also receive information the same way?
|
50
|
+
|
51
|
+
## Configuration
|
52
|
+
|
53
|
+
Hookie was written such that it can be configured using Gitolite's ability to set git config keys via the gitolite-admin repository.
|
54
|
+
|
55
|
+
You can set "global" config keys using the special repository @all in gitolite, and override or set project specific keys within the repository itself. This makes it easy to, for example, set your HipChat API key for all repos, but then set the room that gets notified in each repo separately - and this can all be done via the gitolite-admin repository, without requiring access to the git server.
|
56
|
+
|
57
|
+
An example conf/gitolite.conf:
|
58
|
+
|
59
|
+
repo gitolite-admin
|
60
|
+
RW+ = marcin
|
61
|
+
|
62
|
+
repo testing
|
63
|
+
RW+ = @all
|
64
|
+
config hookie.core.allowedplugins = hipchat,jenkins
|
65
|
+
config hookie.hipchat.room = 123456
|
66
|
+
config hookie.jenkins.url = http://jenkins.example.com/job/Test%20Job/build?token=test
|
67
|
+
config hookie.jenkins.branches=develop
|
68
|
+
config hookie.jenkins.auth=jenkins:TOKEN
|
69
|
+
|
70
|
+
repo @all
|
71
|
+
config hookie.hipchat.apikey = APIKEY
|
72
|
+
config hookie.hipchat.from = "Git Test"
|
73
|
+
config hookie.core.web.commit = https://fisheye.example.com/changelog/%REPO%?cs=%COMMIT%
|
74
|
+
config hookie.core.web.browse = https://fisheye.example.com/browse/%REPO%
|
75
|
+
|
76
|
+
The following core keys are used by the framework:
|
77
|
+
|
78
|
+
* **hookie.core.allowedplugins** - comma separated list of plugins that are run for this repo - by default NO plugins get run.
|
79
|
+
* **hookie.core.repo.name** - set the name of the repository used when generating URLs or for display purposes. Normally it can be determined from the path and the key isn't required
|
80
|
+
* **hookie.core.web.browse** - URL to a web based repo browser. The special variable %REPO% will be replaced with the repo name.
|
81
|
+
* **hookie.core.web.commit** - URL to a web based view of a single commit. The special variables %REPO% and %COMMIT% will be replaced with the repo name and commit id respectively.
|
82
|
+
* **hookie.core.web.proxy** - optional proxy to use when making HTTP calls in plugins (example syntax: "proxy.example.com:8080")
|
83
|
+
|
84
|
+
## Plugins
|
85
|
+
|
86
|
+
The following plugins are shipped with Hookie:
|
87
|
+
|
88
|
+
* HipChat - posts a notification to a HipChat room in response to a commit
|
89
|
+
* Jenkins - triggers a Jenkins build in reponse to a commit
|
90
|
+
|
91
|
+
### HipChat
|
92
|
+
|
93
|
+
The following config keys apply to the HipChat plugin:
|
94
|
+
|
95
|
+
* **hookie.hipchat.apikey** - your HipChat API key
|
96
|
+
* **hookie.hipchat.room** - the HipChat room to post your message to - you can get a list of rooms using the following API call:
|
97
|
+
http://api.hipchat.com/v1/rooms/list?auth_token=YOUR_TOKEN
|
98
|
+
* **hookie.hipchat.from** - who the notification appears from (default: git)
|
99
|
+
* **hookie.hipchat.notify** - set to 1 to trigger a notification (default: 0)
|
100
|
+
|
101
|
+
### Jenkins
|
102
|
+
|
103
|
+
The following config keys apply to the Jenkins plugin:
|
104
|
+
|
105
|
+
* **hookie.jenkins.url** - the trigger URL to be called for this repostiory (you should set this per-repository)
|
106
|
+
* **hookie.jenkins.branches** - only trigger a build if the change set includes commits to the branches in this list. Comma separated. Default is to build for any commit.
|
107
|
+
* **hookie.jenkins.auth** - the username:token combo to use to authenticate with Jenkins
|
108
|
+
|
109
|
+
## Writing Plugins
|
110
|
+
|
111
|
+
Hookie was designed to be extensible by writing your own plugins.
|
112
|
+
|
113
|
+
A minimal plugin is:
|
114
|
+
|
115
|
+
require 'hookie/plugins/base_plugin'
|
116
|
+
|
117
|
+
module Hookie
|
118
|
+
module Plugin
|
119
|
+
class EmptyPlugin < BasePlugin
|
120
|
+
def post_receive
|
121
|
+
log "Empty Plugin - post_receive"
|
122
|
+
end
|
123
|
+
end
|
124
|
+
end
|
125
|
+
end
|
126
|
+
|
127
|
+
BasePlugin provides useful instance variables such as config and framework.config is a symbol keyed hash of your plugins config. By convention your config key is the lowercased first part of your plugin name (eg. EmptyPlugin -> empty), so config keys are, for example, hookie.empty.blah
|
128
|
+
|
129
|
+
Your plugin will be called if it has a method matching the name of the hook being run (eg. "post_receive" above). See the HipChat and Jenkins plugins for vaguely more complex examples.
|
data/Rakefile
ADDED
data/bin/hookie
ADDED
data/hookie.gemspec
ADDED
@@ -0,0 +1,27 @@
|
|
1
|
+
# -*- encoding: utf-8 -*-
|
2
|
+
lib = File.expand_path('../lib', __FILE__)
|
3
|
+
$LOAD_PATH.unshift(lib) unless $LOAD_PATH.include?(lib)
|
4
|
+
|
5
|
+
require 'hookie'
|
6
|
+
|
7
|
+
Gem::Specification.new do |gem|
|
8
|
+
gem.name = "hookie"
|
9
|
+
gem.version = Hookie::VERSION
|
10
|
+
gem.authors = ["Marcin Szczepanski"]
|
11
|
+
gem.email = ["marcins@webqem.com"]
|
12
|
+
gem.description = %q{Hookie provides a way to write git hooks with ruby without too much worrying about any of the plumbing required, you can easily write your own plugins and focus on the core of your functionality.
|
13
|
+
|
14
|
+
Hookie includes plugins for Jenkins and HipChat.}
|
15
|
+
gem.summary = %q{Framework for writing gitolite/git hooks with Ruby}
|
16
|
+
gem.homepage = "https://github.com/marcins/hookie"
|
17
|
+
|
18
|
+
gem.files = `git ls-files`.split($/)
|
19
|
+
gem.executables = gem.files.grep(%r{^bin/}).map{ |f| File.basename(f) }
|
20
|
+
gem.test_files = gem.files.grep(%r{^(test|spec|features)/})
|
21
|
+
gem.require_paths = ["lib"]
|
22
|
+
|
23
|
+
gem.add_dependency 'grit', '~> 2.5.0'
|
24
|
+
gem.add_dependency 'diff-lcs', '~> 1.1.3'
|
25
|
+
|
26
|
+
gem.add_development_dependency 'rspec', '~> 2.12.0'
|
27
|
+
end
|
@@ -0,0 +1,100 @@
|
|
1
|
+
module Hookie
|
2
|
+
|
3
|
+
class Framework
|
4
|
+
|
5
|
+
require 'grit'
|
6
|
+
|
7
|
+
attr_reader :changes
|
8
|
+
|
9
|
+
def self.hook(hook)
|
10
|
+
hookie = Hookie::Framework.new hook, ARGV[1] || Dir.getwd
|
11
|
+
hookie.run_plugins(hook)
|
12
|
+
end
|
13
|
+
|
14
|
+
def initialize(hook, repo_path)
|
15
|
+
@repo = Grit::Repo.new(repo_path)
|
16
|
+
read_changes
|
17
|
+
end
|
18
|
+
|
19
|
+
def run_plugins(hook)
|
20
|
+
# we are only allowed to run if the plugin is in the list of allowed
|
21
|
+
# plugins
|
22
|
+
unless config['hookie.core.allowedplugins']
|
23
|
+
exit 255
|
24
|
+
end
|
25
|
+
|
26
|
+
Dir.glob(File.join(File.join(File.dirname(__FILE__),"plugins"), "*_plugin.rb")) do |filename|
|
27
|
+
begin
|
28
|
+
require filename
|
29
|
+
rescue LoadError => e
|
30
|
+
puts "Unable to load plugin #{filename}: #{e}"
|
31
|
+
end
|
32
|
+
end
|
33
|
+
|
34
|
+
Hookie::Plugin.constants.each do |constant|
|
35
|
+
clazz = Hookie::Plugin.const_get(constant)
|
36
|
+
if clazz < Hookie::BasePlugin
|
37
|
+
plugin = clazz.new(self)
|
38
|
+
if config['hookie.core.allowedplugins'].include?(plugin.config_key) and
|
39
|
+
plugin.respond_to?(hook) and
|
40
|
+
plugin.should_run?
|
41
|
+
begin
|
42
|
+
plugin.send(hook)
|
43
|
+
rescue Exception => e
|
44
|
+
log plugin, "ERROR: plugin threw an exception: #{e}"
|
45
|
+
end
|
46
|
+
end
|
47
|
+
end
|
48
|
+
end
|
49
|
+
end
|
50
|
+
|
51
|
+
def log(plugin, message)
|
52
|
+
timestamp = Time.now.strftime("%Y-%m-%d %H:%M:%S")
|
53
|
+
puts "[#{timestamp}] #{plugin}: #{message}"
|
54
|
+
end
|
55
|
+
|
56
|
+
def repo_url
|
57
|
+
if config['hookie.core.web.browse']
|
58
|
+
config['hookie.core.web.browse'].gsub("%REPO%", repo_name)
|
59
|
+
end
|
60
|
+
end
|
61
|
+
|
62
|
+
def commit_url(commit)
|
63
|
+
if config['hookie.core.web.commit']
|
64
|
+
config['hookie.core.web.commit'].gsub("%REPO%", repo_name).gsub("%COMMIT%", commit.id)
|
65
|
+
end
|
66
|
+
end
|
67
|
+
|
68
|
+
def repo_name
|
69
|
+
if config['hookie.core.repo.name']
|
70
|
+
config['hookie.core.repo.name']
|
71
|
+
elsif @repo.bare
|
72
|
+
File.basename(@repo.path, ".git")
|
73
|
+
else
|
74
|
+
File.basename(File.expand_path(File.join(@repo.path, "..")))
|
75
|
+
end
|
76
|
+
end
|
77
|
+
|
78
|
+
def head_names_for_commit(commit)
|
79
|
+
@repo.heads.collect { |head| head.name if head.commit.id == commit.id }.compact
|
80
|
+
end
|
81
|
+
|
82
|
+
def config
|
83
|
+
@repo.config
|
84
|
+
end
|
85
|
+
|
86
|
+
private
|
87
|
+
def read_changes
|
88
|
+
@changes = []
|
89
|
+
STDIN.each_line do |line|
|
90
|
+
parts = line.split(" ")
|
91
|
+
@changes << {
|
92
|
+
old_hash: parts[0],
|
93
|
+
new_hash: parts[1],
|
94
|
+
ref: parts[2],
|
95
|
+
commit: @repo.commits(parts[1], 1)[0]
|
96
|
+
}
|
97
|
+
end
|
98
|
+
end
|
99
|
+
end
|
100
|
+
end
|
@@ -0,0 +1,40 @@
|
|
1
|
+
module Hookie
|
2
|
+
class BasePlugin
|
3
|
+
|
4
|
+
attr_reader :config
|
5
|
+
|
6
|
+
def initialize(framework)
|
7
|
+
@framework = framework
|
8
|
+
|
9
|
+
@config = {}
|
10
|
+
@framework.config.keys.each do |k|
|
11
|
+
if k.start_with?("hookie.#{self.config_key}")
|
12
|
+
@config[k.split(".")[2..-1].join("_").to_sym] = @framework.config[k]
|
13
|
+
end
|
14
|
+
end
|
15
|
+
|
16
|
+
end
|
17
|
+
|
18
|
+
def log(message)
|
19
|
+
@framework.log(self, message)
|
20
|
+
end
|
21
|
+
|
22
|
+
def should_run?
|
23
|
+
true
|
24
|
+
end
|
25
|
+
|
26
|
+
def to_s
|
27
|
+
plugin_name
|
28
|
+
end
|
29
|
+
|
30
|
+
def config_key
|
31
|
+
plugin_name.downcase
|
32
|
+
end
|
33
|
+
|
34
|
+
protected
|
35
|
+
def plugin_name
|
36
|
+
@plugin_name ||= self.class.to_s[/(.*::)?(\w+)Plugin/, 2]
|
37
|
+
end
|
38
|
+
|
39
|
+
end
|
40
|
+
end
|
@@ -0,0 +1,88 @@
|
|
1
|
+
require_relative 'base_plugin'
|
2
|
+
|
3
|
+
require 'time'
|
4
|
+
require 'net/https'
|
5
|
+
require 'json'
|
6
|
+
|
7
|
+
module Hookie
|
8
|
+
module Plugin
|
9
|
+
class HipChatPlugin < BasePlugin
|
10
|
+
def to_s
|
11
|
+
"HipChat Notifier"
|
12
|
+
end
|
13
|
+
|
14
|
+
def should_run?
|
15
|
+
warnings = []
|
16
|
+
if @framework.changes.empty?
|
17
|
+
log "No changes"
|
18
|
+
return false
|
19
|
+
end
|
20
|
+
warnings << "hookie.hipchat.apikey not set!" unless @config[:apikey]
|
21
|
+
warnings << "hookie.hipchat.room not set!" unless @config[:room]
|
22
|
+
|
23
|
+
log warnings.join(", ") unless warnings.empty?
|
24
|
+
|
25
|
+
warnings.empty?
|
26
|
+
end
|
27
|
+
|
28
|
+
def post_receive
|
29
|
+
log "Sending message to HipChat ... "
|
30
|
+
response = {}
|
31
|
+
#log "Message: #{format_message}"
|
32
|
+
response = speak format_message
|
33
|
+
if response[:status]
|
34
|
+
log "Message sent to HipChat"
|
35
|
+
elsif response[:error]
|
36
|
+
log "Message end failed: #{response[:error][:message]}"
|
37
|
+
else
|
38
|
+
log "Unknown response"
|
39
|
+
end
|
40
|
+
end
|
41
|
+
|
42
|
+
private
|
43
|
+
def format_message
|
44
|
+
# Commits just pushed to <a href="repo-url">repo-name</a>:
|
45
|
+
# hash (branch) Author: message
|
46
|
+
message = ""
|
47
|
+
message << "Commits just pushed to "
|
48
|
+
message << if @framework.repo_url
|
49
|
+
"<a href='#{@framework.repo_url}'>#{@framework.repo_name}</a>"
|
50
|
+
else
|
51
|
+
@framework.repo_name
|
52
|
+
end
|
53
|
+
message << ":<br/>"
|
54
|
+
@framework.changes.each do |change|
|
55
|
+
commit = change[:commit]
|
56
|
+
|
57
|
+
message << if @framework.commit_url(commit)
|
58
|
+
"<a href='#{@framework.commit_url(commit)}'>#{commit.id_abbrev}</a>"
|
59
|
+
else
|
60
|
+
"#{commit.id_abbrev}"
|
61
|
+
end
|
62
|
+
head_names = @framework.head_names_for_commit(commit).join(", ")
|
63
|
+
message << " (#{head_names})" unless head_names.empty?
|
64
|
+
message << " #{commit.author}: #{commit.short_message}"
|
65
|
+
message << "<br/>"
|
66
|
+
end
|
67
|
+
message
|
68
|
+
end
|
69
|
+
|
70
|
+
private
|
71
|
+
def speak(message)
|
72
|
+
uri = URI.parse("https://api.hipchat.com/")
|
73
|
+
http = Net::HTTP.new(uri.host, uri.port, @config[:proxyaddress], @config[:proxyport])
|
74
|
+
http.use_ssl = true
|
75
|
+
http.verify_mode = OpenSSL::SSL::VERIFY_NONE
|
76
|
+
request = Net::HTTP::Post.new("/v1/rooms/message")
|
77
|
+
request.set_form_data({"message" => message,
|
78
|
+
"auth_token" => @config[:apikey],
|
79
|
+
"room_id" => @config[:room],
|
80
|
+
"notify" => @config[:notify] || 0,
|
81
|
+
"from" => @config[:from] || "git"})
|
82
|
+
|
83
|
+
JSON.parse(http.request(request).body, {symbolize_names: true})
|
84
|
+
end
|
85
|
+
|
86
|
+
end
|
87
|
+
end
|
88
|
+
end
|
@@ -0,0 +1,39 @@
|
|
1
|
+
require_relative "base_plugin"
|
2
|
+
|
3
|
+
module Hookie
|
4
|
+
module Plugin
|
5
|
+
class JenkinsPlugin < BasePlugin
|
6
|
+
|
7
|
+
def should_run?
|
8
|
+
#puts "Config: #{config}"
|
9
|
+
unless config[:url]
|
10
|
+
log "Jenkins URL not configured!"
|
11
|
+
return false
|
12
|
+
end
|
13
|
+
|
14
|
+
if config[:branches]
|
15
|
+
allowed_branches = config[:branches].split(",")
|
16
|
+
commits = @framework.changes.map { |change| change[:commit] }
|
17
|
+
branches = commits.collect { |commit| @framework.head_names_for_commit(commit) }
|
18
|
+
branches.flatten!
|
19
|
+
if (branches & allowed_branches).empty?
|
20
|
+
log "No commits on matching branches (#{allowed_branches.join(', ')})"
|
21
|
+
return false
|
22
|
+
end
|
23
|
+
end
|
24
|
+
return true
|
25
|
+
end
|
26
|
+
|
27
|
+
def post_receive
|
28
|
+
uri = URI.parse(config[:url])
|
29
|
+
http = Net::HTTP.new(uri.host, uri.port)
|
30
|
+
request = Net::HTTP::Get.new(uri.request_uri)
|
31
|
+
if config[:auth]
|
32
|
+
request.basic_auth(*config[:auth].split(":"))
|
33
|
+
end
|
34
|
+
response = http.request(request)
|
35
|
+
log "Response: #{response.body}"
|
36
|
+
end
|
37
|
+
end
|
38
|
+
end
|
39
|
+
end
|
data/lib/hookie.rb
ADDED
@@ -0,0 +1,43 @@
|
|
1
|
+
require 'spec_helper'
|
2
|
+
require 'hookie'
|
3
|
+
require 'hookie/plugins/base_plugin'
|
4
|
+
|
5
|
+
class ThisHasALongNamePlugin < Hookie::BasePlugin
|
6
|
+
end
|
7
|
+
|
8
|
+
describe Hookie::BasePlugin do
|
9
|
+
before :each do
|
10
|
+
@hookie = double(Hookie::Framework)
|
11
|
+
@config = double(Grit::Config)
|
12
|
+
@config.stub(:keys).and_return([])
|
13
|
+
@hookie.stub(:config).and_return(@config)
|
14
|
+
end
|
15
|
+
context "base plugin" do
|
16
|
+
before :each do
|
17
|
+
@plugin = Hookie::BasePlugin.new @hookie
|
18
|
+
end
|
19
|
+
it "should initialise" do
|
20
|
+
|
21
|
+
end
|
22
|
+
it "should correctly derive the plugin name" do
|
23
|
+
@plugin.to_s.should eq "Base"
|
24
|
+
@plugin.config_key.should eq "base"
|
25
|
+
|
26
|
+
plugin = ThisHasALongNamePlugin.new @hookie
|
27
|
+
plugin.to_s.should eq "ThisHasALongName"
|
28
|
+
plugin.config_key.should eq "thishasalongname"
|
29
|
+
end
|
30
|
+
|
31
|
+
it "should read the correct config" do
|
32
|
+
@config.should_receive(:keys).once.and_return(["core.key","hookie.base.mykey"])
|
33
|
+
@config.should_receive('[]').with('hookie.base.mykey').and_return("value")
|
34
|
+
@plugin = Hookie::BasePlugin.new @hookie
|
35
|
+
@plugin.config.should have(1).items
|
36
|
+
end
|
37
|
+
|
38
|
+
it "should send log messages" do
|
39
|
+
@hookie.should_receive(:log).once.with(@plugin, "message")
|
40
|
+
@plugin.log "message"
|
41
|
+
end
|
42
|
+
end
|
43
|
+
end
|
@@ -0,0 +1,66 @@
|
|
1
|
+
require 'spec_helper'
|
2
|
+
|
3
|
+
describe Hookie do
|
4
|
+
before(:each) do
|
5
|
+
@repo = double('Grit::Repo')
|
6
|
+
@config = double('Grit::Config')
|
7
|
+
Grit::Repo.should_receive(:new).once.and_return(@repo)
|
8
|
+
@repo.stub(:commits).and_return([])
|
9
|
+
@repo.stub(:config).and_return(@config)
|
10
|
+
STDIN.should_receive(:each_line).and_yield("bf6245eb3f1fa4cadff4299cb45e3ed85bc3337e b37c9728362ec39ce57adaab6ad9f0225d3513fe refs/heads/master").and_yield("0000000000000000000000000000000000000000 09e6a76d20ae8c8e1d40ce21b6ea586ff860e5d3 refs/heads/develop")
|
11
|
+
end
|
12
|
+
|
13
|
+
context 'initialisation' do
|
14
|
+
it "intialises" do
|
15
|
+
hookie = Hookie::Framework.new "test_hook", Dir.getwd
|
16
|
+
end
|
17
|
+
|
18
|
+
it "reads changes" do
|
19
|
+
commit = double('Grit::Commit')
|
20
|
+
@repo.should_receive(:commits).with("b37c9728362ec39ce57adaab6ad9f0225d3513fe", 1).once.and_return([commit])
|
21
|
+
|
22
|
+
hookie = Hookie::Framework.new "test_hook", Dir.getwd
|
23
|
+
|
24
|
+
hookie.changes.should have(2).items
|
25
|
+
change = hookie.changes[0]
|
26
|
+
|
27
|
+
change[:old_hash].should eq "bf6245eb3f1fa4cadff4299cb45e3ed85bc3337e"
|
28
|
+
change[:new_hash].should eq "b37c9728362ec39ce57adaab6ad9f0225d3513fe"
|
29
|
+
change[:ref].should eq "refs/heads/master"
|
30
|
+
|
31
|
+
change[:commit].should be commit
|
32
|
+
end
|
33
|
+
end
|
34
|
+
|
35
|
+
context "helpers" do
|
36
|
+
before(:each) do
|
37
|
+
@hookie = Hookie::Framework.new "test_hook", Dir.getwd
|
38
|
+
end
|
39
|
+
|
40
|
+
it "should not return a repo browsing url if it's not set" do
|
41
|
+
@repo.should_receive(:config).once.and_return(@config)
|
42
|
+
@config.should_receive('[]').with('hookie.core.web.browse').and_return(nil)
|
43
|
+
@hookie.repo_url.should be_nil
|
44
|
+
end
|
45
|
+
|
46
|
+
it "should return a repo browsing url if it's set" do
|
47
|
+
@repo.config.should_receive('[]').with("hookie.core.web.browse").at_least(:once).and_return("TEST %REPO% TEST")
|
48
|
+
@repo.config.should_receive('[]').with("hookie.core.repo.name").at_least(:once).and_return("testrepo")
|
49
|
+
@hookie.repo_url.should eq "TEST testrepo TEST"
|
50
|
+
end
|
51
|
+
|
52
|
+
it "should not return a commit url if it's not set" do
|
53
|
+
@repo.config.should_receive('[]').with("hookie.core.web.commit").at_least(:once).and_return(nil)
|
54
|
+
commit = double('Grit::Commit')
|
55
|
+
@hookie.commit_url(commit).should be_nil
|
56
|
+
end
|
57
|
+
|
58
|
+
it "should return a commit url if it's set" do
|
59
|
+
@repo.config.should_receive('[]').with("hookie.core.web.commit").at_least(:once).and_return("TEST %REPO% TEST %COMMIT%")
|
60
|
+
@repo.config.should_receive('[]').with("hookie.core.repo.name").at_least(:once).and_return("testrepo")
|
61
|
+
commit = double('Grit::Commit')
|
62
|
+
commit.stub(:id).and_return("ABC")
|
63
|
+
@hookie.commit_url(commit).should eq "TEST testrepo TEST ABC"
|
64
|
+
end
|
65
|
+
end
|
66
|
+
end
|
@@ -0,0 +1,51 @@
|
|
1
|
+
require 'spec_helper'
|
2
|
+
require 'hookie'
|
3
|
+
require 'hookie/plugins/hipchat_plugin'
|
4
|
+
|
5
|
+
describe Hookie::Plugin::HipChatPlugin do
|
6
|
+
before :each do
|
7
|
+
@hookie = double(Hookie::Framework)
|
8
|
+
@config = double(Grit::Config)
|
9
|
+
@config.stub(:keys).and_return([])
|
10
|
+
|
11
|
+
@hookie.stub(:config).and_return(@config)
|
12
|
+
@hookie.stub(:log)
|
13
|
+
end
|
14
|
+
context "HipChat plugin" do
|
15
|
+
before :each do
|
16
|
+
@plugin = Hookie::Plugin::HipChatPlugin.new @hookie
|
17
|
+
end
|
18
|
+
|
19
|
+
context "config" do
|
20
|
+
it "shouldn't run only when there are no changes" do
|
21
|
+
@hookie.should_receive(:changes).once.and_return([])
|
22
|
+
@plugin.should_run?.should be_false
|
23
|
+
end
|
24
|
+
|
25
|
+
it "shouldn't run when there are changes but config is missing" do
|
26
|
+
@hookie.should_receive(:changes).once.and_return(["something"])
|
27
|
+
@hookie.should_receive(:log).once
|
28
|
+
@plugin.should_run?.should be_false
|
29
|
+
end
|
30
|
+
|
31
|
+
it "should run when there are no changes and config is correct" do
|
32
|
+
@config.should_receive(:keys).and_return(['hookie.hipchat.apikey','hookie.hipchat.room'])
|
33
|
+
@config.should_receive('[]').with('hookie.hipchat.apikey').and_return("ABC123")
|
34
|
+
@config.should_receive('[]').with('hookie.hipchat.room').and_return("1234")
|
35
|
+
plugin = Hookie::Plugin::HipChatPlugin.new @hookie
|
36
|
+
@hookie.should_receive(:changes).once.and_return(["something"])
|
37
|
+
@hookie.should_not_receive(:log)
|
38
|
+
plugin.should_run?.should be_true
|
39
|
+
end
|
40
|
+
end
|
41
|
+
|
42
|
+
it "sends the correct message" do
|
43
|
+
# FIXME this neds to be better
|
44
|
+
@plugin.stub(:format_message).and_return("message")
|
45
|
+
@plugin.should_receive(:speak).with("message").and_return({"status" => "sent"})
|
46
|
+
@hookie.should_receive(:log).twice
|
47
|
+
@plugin.post_receive
|
48
|
+
end
|
49
|
+
|
50
|
+
end
|
51
|
+
end
|
@@ -0,0 +1,48 @@
|
|
1
|
+
require 'spec_helper'
|
2
|
+
require 'hookie'
|
3
|
+
require 'hookie/plugins/jenkins_plugin'
|
4
|
+
|
5
|
+
describe Hookie::Plugin::JenkinsPlugin do
|
6
|
+
before :each do
|
7
|
+
@hookie = double(Hookie::Framework)
|
8
|
+
@config = double(Grit::Config)
|
9
|
+
@config.stub(:keys).and_return([])
|
10
|
+
|
11
|
+
@hookie.stub(:config).and_return(@config)
|
12
|
+
@hookie.stub(:log)
|
13
|
+
|
14
|
+
@plugin = Hookie::Plugin::JenkinsPlugin.new @hookie
|
15
|
+
end
|
16
|
+
context "Jenkins plugin" do
|
17
|
+
|
18
|
+
context "config" do
|
19
|
+
it "shouldn't run when not configured" do
|
20
|
+
@plugin.stub(:config).and_return({})
|
21
|
+
@plugin.should_run?.should be_false
|
22
|
+
end
|
23
|
+
it "should run when it is configured" do
|
24
|
+
@plugin.should_receive(:config).at_least(:once).and_return({url: "foo"})
|
25
|
+
@plugin.should_run?.should be_true
|
26
|
+
end
|
27
|
+
it "should check for branches if configured" do
|
28
|
+
commit = double("Grit::Commit")
|
29
|
+
@hookie.should_receive(:changes).at_least(:once).and_return([
|
30
|
+
{old_hash: "", new_hash: "", ref: "", commit: commit}
|
31
|
+
])
|
32
|
+
@hookie.should_receive(:head_names_for_commit).with(commit).and_return(['develop'])
|
33
|
+
@plugin.should_receive(:config).at_least(:once).and_return({
|
34
|
+
url: "foo",
|
35
|
+
branches: "develop,master"
|
36
|
+
})
|
37
|
+
@plugin.should_run?.should be_true
|
38
|
+
|
39
|
+
@hookie.should_receive(:head_names_for_commit).with(commit).and_return(['develop','foob'])
|
40
|
+
@plugin.should_run?.should be_true
|
41
|
+
|
42
|
+
@hookie.should_receive(:head_names_for_commit).with(commit).and_return(['develop','master'])
|
43
|
+
@plugin.should_run?.should be_true
|
44
|
+
|
45
|
+
end
|
46
|
+
end
|
47
|
+
end
|
48
|
+
end
|
data/spec/spec_helper.rb
ADDED
metadata
ADDED
@@ -0,0 +1,120 @@
|
|
1
|
+
--- !ruby/object:Gem::Specification
|
2
|
+
name: hookie
|
3
|
+
version: !ruby/object:Gem::Version
|
4
|
+
version: 1.0.0
|
5
|
+
prerelease:
|
6
|
+
platform: ruby
|
7
|
+
authors:
|
8
|
+
- Marcin Szczepanski
|
9
|
+
autorequire:
|
10
|
+
bindir: bin
|
11
|
+
cert_chain: []
|
12
|
+
date: 2013-02-12 00:00:00.000000000 Z
|
13
|
+
dependencies:
|
14
|
+
- !ruby/object:Gem::Dependency
|
15
|
+
name: grit
|
16
|
+
requirement: !ruby/object:Gem::Requirement
|
17
|
+
none: false
|
18
|
+
requirements:
|
19
|
+
- - ~>
|
20
|
+
- !ruby/object:Gem::Version
|
21
|
+
version: 2.5.0
|
22
|
+
type: :runtime
|
23
|
+
prerelease: false
|
24
|
+
version_requirements: !ruby/object:Gem::Requirement
|
25
|
+
none: false
|
26
|
+
requirements:
|
27
|
+
- - ~>
|
28
|
+
- !ruby/object:Gem::Version
|
29
|
+
version: 2.5.0
|
30
|
+
- !ruby/object:Gem::Dependency
|
31
|
+
name: diff-lcs
|
32
|
+
requirement: !ruby/object:Gem::Requirement
|
33
|
+
none: false
|
34
|
+
requirements:
|
35
|
+
- - ~>
|
36
|
+
- !ruby/object:Gem::Version
|
37
|
+
version: 1.1.3
|
38
|
+
type: :runtime
|
39
|
+
prerelease: false
|
40
|
+
version_requirements: !ruby/object:Gem::Requirement
|
41
|
+
none: false
|
42
|
+
requirements:
|
43
|
+
- - ~>
|
44
|
+
- !ruby/object:Gem::Version
|
45
|
+
version: 1.1.3
|
46
|
+
- !ruby/object:Gem::Dependency
|
47
|
+
name: rspec
|
48
|
+
requirement: !ruby/object:Gem::Requirement
|
49
|
+
none: false
|
50
|
+
requirements:
|
51
|
+
- - ~>
|
52
|
+
- !ruby/object:Gem::Version
|
53
|
+
version: 2.12.0
|
54
|
+
type: :development
|
55
|
+
prerelease: false
|
56
|
+
version_requirements: !ruby/object:Gem::Requirement
|
57
|
+
none: false
|
58
|
+
requirements:
|
59
|
+
- - ~>
|
60
|
+
- !ruby/object:Gem::Version
|
61
|
+
version: 2.12.0
|
62
|
+
description: ! "Hookie provides a way to write git hooks with ruby without too much
|
63
|
+
worrying about any of the plumbing required, you can easily write your own plugins
|
64
|
+
and focus on the core of your functionality. \n\n Hookie includes plugins for
|
65
|
+
Jenkins and HipChat."
|
66
|
+
email:
|
67
|
+
- marcins@webqem.com
|
68
|
+
executables:
|
69
|
+
- hookie
|
70
|
+
extensions: []
|
71
|
+
extra_rdoc_files: []
|
72
|
+
files:
|
73
|
+
- .gitignore
|
74
|
+
- Gemfile
|
75
|
+
- LICENSE.txt
|
76
|
+
- README.md
|
77
|
+
- Rakefile
|
78
|
+
- bin/hookie
|
79
|
+
- hookie.gemspec
|
80
|
+
- lib/hookie.rb
|
81
|
+
- lib/hookie/framework.rb
|
82
|
+
- lib/hookie/plugins/base_plugin.rb
|
83
|
+
- lib/hookie/plugins/empty_plugin.rb
|
84
|
+
- lib/hookie/plugins/hipchat_plugin.rb
|
85
|
+
- lib/hookie/plugins/jenkins_plugin.rb
|
86
|
+
- spec/base_plugin_spec.rb
|
87
|
+
- spec/framework_spec.rb
|
88
|
+
- spec/hipchat_plugin_spec.rb
|
89
|
+
- spec/jenkins_plugin_spec.rb
|
90
|
+
- spec/spec_helper.rb
|
91
|
+
homepage: https://github.com/marcins/hookie
|
92
|
+
licenses: []
|
93
|
+
post_install_message:
|
94
|
+
rdoc_options: []
|
95
|
+
require_paths:
|
96
|
+
- lib
|
97
|
+
required_ruby_version: !ruby/object:Gem::Requirement
|
98
|
+
none: false
|
99
|
+
requirements:
|
100
|
+
- - ! '>='
|
101
|
+
- !ruby/object:Gem::Version
|
102
|
+
version: '0'
|
103
|
+
required_rubygems_version: !ruby/object:Gem::Requirement
|
104
|
+
none: false
|
105
|
+
requirements:
|
106
|
+
- - ! '>='
|
107
|
+
- !ruby/object:Gem::Version
|
108
|
+
version: '0'
|
109
|
+
requirements: []
|
110
|
+
rubyforge_project:
|
111
|
+
rubygems_version: 1.8.23
|
112
|
+
signing_key:
|
113
|
+
specification_version: 3
|
114
|
+
summary: Framework for writing gitolite/git hooks with Ruby
|
115
|
+
test_files:
|
116
|
+
- spec/base_plugin_spec.rb
|
117
|
+
- spec/framework_spec.rb
|
118
|
+
- spec/hipchat_plugin_spec.rb
|
119
|
+
- spec/jenkins_plugin_spec.rb
|
120
|
+
- spec/spec_helper.rb
|