flowdock-git-hook 0.1.0

Sign up to get free protection for your applications and to get access to all the features.
data/.document ADDED
@@ -0,0 +1,5 @@
1
+ lib/**/*.rb
2
+ bin/*
3
+ -
4
+ features/**/*.feature
5
+ LICENSE.txt
data/.rspec ADDED
@@ -0,0 +1 @@
1
+ --color
data/Gemfile ADDED
@@ -0,0 +1,12 @@
1
+ source "http://rubygems.org"
2
+
3
+ gem "grit", ">= 2.4.1"
4
+ gem "multi_json"
5
+
6
+ group :development do
7
+ gem "rspec", "~> 2.6.0"
8
+ gem "bundler", "~> 1.0.0"
9
+ gem "jeweler", "~> 1.6.2"
10
+ gem "rcov", ">= 0"
11
+ gem "webmock", ">= 1.6.4"
12
+ end
data/LICENSE.txt ADDED
@@ -0,0 +1,20 @@
1
+ Copyright (c) 2011 Flowdock Ltd.
2
+
3
+ Permission is hereby granted, free of charge, to any person obtaining
4
+ a copy of this software and associated documentation files (the
5
+ "Software"), to deal in the Software without restriction, including
6
+ without limitation the rights to use, copy, modify, merge, publish,
7
+ distribute, sublicense, and/or sell copies of the Software, and to
8
+ permit persons to whom the Software is furnished to do so, subject to
9
+ the following conditions:
10
+
11
+ The above copyright notice and this permission notice shall be
12
+ included in all copies or substantial portions of the Software.
13
+
14
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
15
+ EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
16
+ MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
17
+ NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
18
+ LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
19
+ OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
20
+ WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
data/README.rdoc ADDED
@@ -0,0 +1,65 @@
1
+ = Flowdock Git Hook
2
+
3
+ Git Post-Receive hook for Flowdock[http://flowdock.com]
4
+
5
+ == Installation
6
+
7
+ First, you need to install this gem. You need to have Ruby 1.8.6 and Rubygems installed in your system.
8
+
9
+ gem install flowdock-git-hook
10
+
11
+ Then, download the post-receive hook file to the hooks directory and make it executable
12
+
13
+ curl -o hooks/post-receive http://github.com/flowdock/flowdock-git-hook/raw/master/post-receive
14
+ chmod +x hooks/post-receive
15
+
16
+ Configure your Flow API tokens to git configuration
17
+
18
+ git config flowdock.token <Flow API token>
19
+
20
+ After this, you should get updates from your git repo every time you push to it.
21
+
22
+ == Advanced usage
23
+
24
+ The git hook allows Flowdock tags to be attached to push messages. If you only need static tags, e.g. git repo name as tag, this can be configured in git config:
25
+
26
+ git config flowdock.tags git,push
27
+
28
+ For programmatic control over tagging, you can change how the hook is called in post-receive hook file.
29
+
30
+ Flowdock::Git.new(ref, before, after, :tags => ["git", "push"])
31
+
32
+ Note that you can also define token as parameter allowing multiple Flows to be notified.
33
+
34
+ Flowdock::Git.new(ref, before, after, :token => "flow-token")
35
+ Flowdock::Git.new(ref, before, after, :token => "another-flow")
36
+
37
+ == Example data
38
+
39
+ The hook uses {GitHub webhook}[http://help.github.com/post-receive-hooks/] format.
40
+
41
+ payload {
42
+ "after": "122b95a8808ea0cf708fb43b400a377c25c35d7f",
43
+ "before": "2a445d1d348d9d45217cb9c89c12b67d3767ce42",
44
+ "commits": [
45
+ {
46
+ "added": [],
47
+ "author": {
48
+ "email": "raine.virta@nodeta.fi",
49
+ "name": "Raine Virta"
50
+ },
51
+ "id": "122b95a8808ea0cf708fb43b400a377c25c35d7f",
52
+ "message": "yeah!",
53
+ "modified": [
54
+ "TEST_FILE"
55
+ ],
56
+ "removed": [],
57
+ "timestamp": "2010-08-11T13:46:39+03:00"
58
+ }
59
+ ],
60
+ "ref": "refs\/heads\/master",
61
+ "ref_name": "master",
62
+ "repository": {
63
+ "name": "testrepo"
64
+ }
65
+ }
data/Rakefile ADDED
@@ -0,0 +1,48 @@
1
+ # encoding: utf-8
2
+
3
+ require 'rubygems'
4
+ require 'bundler'
5
+ begin
6
+ Bundler.setup(:default, :development)
7
+ rescue Bundler::BundlerError => e
8
+ $stderr.puts e.message
9
+ $stderr.puts "Run `bundle install` to install missing gems"
10
+ exit e.status_code
11
+ end
12
+ require 'rake'
13
+
14
+ require 'jeweler'
15
+ Jeweler::Tasks.new do |gem|
16
+ # gem is a Gem::Specification... see http://docs.rubygems.org/read/chapter/20 for more options
17
+ gem.name = "flowdock-git-hook"
18
+ gem.homepage = "http://github.com/flowdock/flowdock-git-hook"
19
+ gem.license = "MIT"
20
+ gem.summary = %Q{Git Post-Receive hook for Flowdock}
21
+ gem.email = "lautis@gmail.com"
22
+ gem.authors = ["Ville Lautanala"]
23
+ # dependencies defined in Gemfile
24
+ end
25
+ Jeweler::RubygemsDotOrgTasks.new
26
+
27
+ require 'rspec/core'
28
+ require 'rspec/core/rake_task'
29
+ RSpec::Core::RakeTask.new(:spec) do |spec|
30
+ spec.pattern = FileList['spec/**/*_spec.rb']
31
+ end
32
+
33
+ RSpec::Core::RakeTask.new(:rcov) do |spec|
34
+ spec.pattern = 'spec/**/*_spec.rb'
35
+ spec.rcov = true
36
+ end
37
+
38
+ task :default => :spec
39
+
40
+ require 'rake/rdoctask'
41
+ Rake::RDocTask.new do |rdoc|
42
+ version = File.exist?('VERSION') ? File.read('VERSION') : ""
43
+
44
+ rdoc.rdoc_dir = 'rdoc'
45
+ rdoc.title = "flowdock-git-hook #{version}"
46
+ rdoc.rdoc_files.include('README*')
47
+ rdoc.rdoc_files.include('lib/**/*.rb')
48
+ end
data/VERSION ADDED
@@ -0,0 +1 @@
1
+ 0.1.0
@@ -0,0 +1 @@
1
+ require 'flowdock/git'
@@ -0,0 +1,74 @@
1
+ require "multi_json"
2
+ require "net/https"
3
+ require "cgi"
4
+
5
+ require "flowdock/git/builder"
6
+
7
+ module Flowdock
8
+ class Git
9
+ class TokenError < StandardError; end
10
+ API_ENDPOINT = "https://api.flowdock.com/v1/git"
11
+
12
+ def initialize(ref, from, to, options = {})
13
+ @ref = ref
14
+ @from = from
15
+ @to = to
16
+
17
+ @options = options
18
+
19
+ @token = options[:token] || config["flowdock.token"] || raise(TokenError.new("Flowdock API token not found"))
20
+ end
21
+
22
+ # Send git push notification to Flowdock
23
+ def post
24
+ uri = URI.parse("#{API_ENDPOINT}/#{([@token] + tags).join('+')}")
25
+ req = Net::HTTP::Post.new(uri.path)
26
+ req.set_form_data(:payload => MultiJson.encode(payload))
27
+ http = Net::HTTP.new(uri.host, uri.port)
28
+
29
+ if uri.scheme == 'https'
30
+ http.verify_mode = OpenSSL::SSL::VERIFY_NONE
31
+ http.use_ssl = true
32
+ end
33
+
34
+ http.start { |http| http.request(req) }
35
+ end
36
+
37
+ # Create and post notification in background process. Avoid blocking the push notification.
38
+ def background_post
39
+ pid = Process.fork
40
+ if pid.nil?
41
+ Grit::Git.with_timeout(600) do
42
+ post # Child
43
+ end
44
+ else
45
+ Process.detach(pid) # Parent
46
+ end
47
+ end
48
+
49
+ private
50
+
51
+ # Flowdock tags attached to the push notification
52
+ def tags
53
+ if @options[:tags]
54
+ @options[:tags]
55
+ else
56
+ config["flowdock.tags"].to_s.split(",")
57
+ end.map do |t|
58
+ CGI.escape(t)
59
+ end
60
+ end
61
+
62
+ def payload
63
+ Builder.new(repo, @ref, @from, @to).to_hash
64
+ end
65
+
66
+ def repo
67
+ @repo ||= Grit::Repo.new(@options[:repo] || Dir.pwd)
68
+ end
69
+
70
+ def config
71
+ @config ||= Grit::Config.new(repo)
72
+ end
73
+ end
74
+ end
@@ -0,0 +1,61 @@
1
+ require "grit"
2
+
3
+ module Flowdock
4
+ class Git
5
+ # Class used to build Git payload
6
+ class Builder
7
+ def initialize(repo, ref, before, after)
8
+ @repo = repo
9
+ @ref = ref
10
+ @before = before
11
+ @after = after
12
+ end
13
+
14
+ def commits
15
+ @repo.commits_between(@before, @after).map do |commit|
16
+ {
17
+ :id => commit.sha,
18
+ :message => commit.message,
19
+ :timestamp => commit.authored_date.iso8601,
20
+ :author => {
21
+ :name => commit.author.name,
22
+ :email => commit.author.email
23
+ },
24
+ :removed => filter(commit.diffs) { |d| d.deleted_file },
25
+ :added => filter(commit.diffs) { |d| d.new_file },
26
+ :modified => filter(commit.diffs) { |d| !d.deleted_file && !d.new_file }
27
+ }
28
+ end
29
+ end
30
+
31
+ def ref_name
32
+ @ref.to_s.sub(/\Arefs\/(heads|tags)\//, '')
33
+ end
34
+
35
+ def to_hash
36
+ {
37
+ :before => @before,
38
+ :after => @after,
39
+ :ref => @ref,
40
+ :commits => commits,
41
+ :ref_name => @ref.to_s.sub(/\Arefs\/(heads|tags)\//, ''),
42
+ :repository => {
43
+ :name => File.basename(Dir.pwd).sub(/\.git$/,'')
44
+ }
45
+ }.merge(if @before == "0000000000000000000000000000000000000000"
46
+ {:created => true}
47
+ elsif @after == "0000000000000000000000000000000000000000"
48
+ {:deleted => true}
49
+ else
50
+ {}
51
+ end)
52
+ end
53
+
54
+ private
55
+
56
+ def filter(diffs)
57
+ diffs.select { |e| yield e }.map { |diff| diff.b_path }
58
+ end
59
+ end
60
+ end
61
+ end
data/post-receive ADDED
@@ -0,0 +1,6 @@
1
+ #!/usr/bin/ruby -w
2
+ require 'rubygems'
3
+ require 'flowdock-git-hook'
4
+
5
+ before, after, ref = gets.split
6
+ Flowdock::Git.new(ref, before, after).background_post
@@ -0,0 +1,92 @@
1
+ require File.expand_path(File.dirname(__FILE__) + '/spec_helper')
2
+
3
+ describe "Git Payload Builder" do
4
+ before :each do
5
+ @repo = Grit::Repo.new(".")
6
+ @before = "7e32af569ba794b0b1c5e4c38fef1d4e2e56be51"
7
+ @after = "a1a94ba4bfa5f855676066861604b8edae1a20f5"
8
+ end
9
+
10
+ it "parses ref name from head" do
11
+ Flowdock::Git::Builder.new(@repo, "refs/heads/master", @before, @after).ref_name.should == "master"
12
+ end
13
+
14
+ it "parses ref name from tag" do
15
+ Flowdock::Git::Builder.new(@repo, "refs/tags/release-1.0", @before, @after).ref_name.should == "release-1.0"
16
+ end
17
+
18
+ it "detects new branch and sets created=true in data" do
19
+ hash = Flowdock::Git::Builder.new(@repo, "refs/heads/master", "0000000000000000000000000000000000000000", @after).to_hash
20
+ hash[:created].should == true
21
+ hash[:deleted].should_not == true
22
+ end
23
+
24
+ it "detects deleted branch and sets deleted=true in data" do
25
+ hash = Flowdock::Git::Builder.new(@repo, "refs/heads/master", @before, "0000000000000000000000000000000000000000").to_hash
26
+ hash[:deleted].should == true
27
+ hash[:created].should_not == true
28
+ end
29
+
30
+ it "doesn't include commits in branch delete" do
31
+ hash = Flowdock::Git::Builder.new(@repo, "refs/heads/master", @before, "0000000000000000000000000000000000000000").to_hash
32
+ hash[:commits].should be_empty
33
+ end
34
+
35
+ describe "data hash" do
36
+ before :each do
37
+ @repo.stub!(:path).and_return("/foo/bar/flowdock-git-hook/.git")
38
+ @hash = Flowdock::Git::Builder.new(@repo, "refs/heads/master", @before, @after).to_hash
39
+ end
40
+
41
+ it "contains before" do
42
+ @hash[:before].should == @before
43
+ end
44
+
45
+ it "contains after" do
46
+ @hash[:after].should == @after
47
+ end
48
+
49
+ it "contains ref" do
50
+ @hash[:ref].should == "refs/heads/master"
51
+ end
52
+
53
+ it "contains ref name" do
54
+ @hash[:ref_name].should == "master"
55
+ end
56
+
57
+ describe "commits" do
58
+ it "contains all changed commits" do
59
+ @hash[:commits].should have(2).items
60
+ end
61
+
62
+ it "has commit author information" do
63
+ @hash[:commits].first[:author][:name].should == "Ville Lautanala"
64
+ @hash[:commits].first[:author][:email].should == "lautis@gmail.com"
65
+ end
66
+
67
+ it "has commit id" do
68
+ @hash[:commits].first[:id].should == "cf4a78c59cf9e06ebd7336900b2a66b85a88b76c"
69
+ end
70
+
71
+ it "puts deleted files in an array" do
72
+ @hash[:commits].first[:removed].should include("spec/flowdock-git-hook_spec.rb")
73
+ end
74
+
75
+ it "puts added files to an array" do
76
+ @hash[:commits].first[:added].should include("lib/flowdock/git.rb")
77
+ end
78
+
79
+ it "detects modified files" do
80
+ @hash[:commits].first[:modified].should_not include("spec/flowdock-git-hook_spec.rb")
81
+ @hash[:commits].first[:modified].should_not include("lib/flowdock/git.rb")
82
+ @hash[:commits].first[:modified].should include("lib/flowdock-git-hook.rb")
83
+ end
84
+ end
85
+
86
+ describe "repository information" do
87
+ it "contains repository name based on file path" do
88
+ @hash[:repository][:name] = "flowdock-git-hook"
89
+ end
90
+ end
91
+ end
92
+ end
@@ -0,0 +1,64 @@
1
+ require File.expand_path(File.dirname(__FILE__) + '/spec_helper')
2
+
3
+ describe "Flowdock Git Hook" do
4
+ it "raises error if git token is not defined" do
5
+ lambda {
6
+ Flowdock::Git.new("refs/heads/master", "random-hash", "random-hash")
7
+ }.should raise_error(Flowdock::Git::TokenError)
8
+ end
9
+
10
+ it "can read token from git config" do
11
+ Grit::Config.stub!(:new).and_return({
12
+ "flowdock.token" => "flowdock-token"
13
+ })
14
+
15
+ lambda {
16
+ Flowdock::Git.new("refs/heads/master", "random-hash", "random-hash")
17
+ }.should_not raise_error
18
+ end
19
+
20
+ it "builds payload" do
21
+ stub_request(:post, "https://api.flowdock.com/v1/git/flowdock-token")
22
+ Flowdock::Git.new("refs/heads/master", "7e32af569ba794b0b1c5e4c38fef1d4e2e56be51", "a1a94ba4bfa5f855676066861604b8edae1a20f5", :token => "flowdock-token").post
23
+ a_request(:post, "https://api.flowdock.com/v1/git/flowdock-token").with { |req|
24
+ req.body.match(/7e32af569ba794b0b1c5e4c38fef1d4e2e56be51/)
25
+ }.should have_been_made
26
+ end
27
+
28
+ describe "Tagging" do
29
+ it "reads tags from initializer parameter" do
30
+ tags = Flowdock::Git.new("ref", "before", "after", :token => "flowdock-token", :tags => ["foo", "bar"]).send(:tags)
31
+ tags.should include("foo", "bar")
32
+ end
33
+
34
+ it "reads tags from gitconfig as fallback" do
35
+ Grit::Config.stub!(:new).and_return({
36
+ "flowdock.tags" => "foo,bar"
37
+ })
38
+ tags = Flowdock::Git.new("ref", "before", "after", :token => "flowdock-token").send(:tags)
39
+ tags.should include("foo", "bar")
40
+ end
41
+
42
+ it "encodes tags suitable for URI" do
43
+ Flowdock::Git.new("ref", "before", "after", :token => "flowdock-token", :tags => "foo%bar").send(:tags).should include("foo%25bar")
44
+ end
45
+ end
46
+ end
47
+
48
+ describe "Flowdock Git Hook", "HTTP Post" do
49
+ before :each do
50
+ @notifier = Flowdock::Git.new("origin/refs/master", "random-hash", "random-hash", :token => "flowdock-token", :tags => ["foo", "bar"])
51
+ @notifier.stub(:payload) { {} }
52
+ stub_request(:post, "https://api.flowdock.com/v1/git/flowdock-token+foo+bar")
53
+ end
54
+
55
+ it "posts to api.flowdock.com" do
56
+ @notifier.post
57
+ a_request(:post, "https://api.flowdock.com/v1/git/flowdock-token+foo+bar").should have_been_made
58
+ end
59
+
60
+ it "sends payload encoded as JSON" do
61
+ @notifier.post
62
+ a_request(:post, "https://api.flowdock.com/v1/git/flowdock-token+foo+bar").with(:body => {:payload => "{}"}).should have_been_made
63
+ end
64
+ end
@@ -0,0 +1,12 @@
1
+ $LOAD_PATH.unshift(File.join(File.dirname(__FILE__), '..', 'lib'))
2
+ $LOAD_PATH.unshift(File.dirname(__FILE__))
3
+ require 'rspec'
4
+ require 'webmock/rspec'
5
+ require 'flowdock-git-hook'
6
+
7
+ # Requires supporting files with custom matchers and macros, etc,
8
+ # in ./support/ and its subdirectories.
9
+ Dir["#{File.dirname(__FILE__)}/support/**/*.rb"].each {|f| require f}
10
+
11
+ RSpec.configure do |config|
12
+ end
metadata ADDED
@@ -0,0 +1,186 @@
1
+ --- !ruby/object:Gem::Specification
2
+ name: flowdock-git-hook
3
+ version: !ruby/object:Gem::Version
4
+ hash: 27
5
+ prerelease:
6
+ segments:
7
+ - 0
8
+ - 1
9
+ - 0
10
+ version: 0.1.0
11
+ platform: ruby
12
+ authors:
13
+ - Ville Lautanala
14
+ autorequire:
15
+ bindir: bin
16
+ cert_chain: []
17
+
18
+ date: 2011-06-21 00:00:00 Z
19
+ dependencies:
20
+ - !ruby/object:Gem::Dependency
21
+ type: :runtime
22
+ requirement: &id001 !ruby/object:Gem::Requirement
23
+ none: false
24
+ requirements:
25
+ - - ">="
26
+ - !ruby/object:Gem::Version
27
+ hash: 29
28
+ segments:
29
+ - 2
30
+ - 4
31
+ - 1
32
+ version: 2.4.1
33
+ prerelease: false
34
+ version_requirements: *id001
35
+ name: grit
36
+ - !ruby/object:Gem::Dependency
37
+ type: :runtime
38
+ requirement: &id002 !ruby/object:Gem::Requirement
39
+ none: false
40
+ requirements:
41
+ - - ">="
42
+ - !ruby/object:Gem::Version
43
+ hash: 3
44
+ segments:
45
+ - 0
46
+ version: "0"
47
+ prerelease: false
48
+ version_requirements: *id002
49
+ name: multi_json
50
+ - !ruby/object:Gem::Dependency
51
+ type: :development
52
+ requirement: &id003 !ruby/object:Gem::Requirement
53
+ none: false
54
+ requirements:
55
+ - - ~>
56
+ - !ruby/object:Gem::Version
57
+ hash: 23
58
+ segments:
59
+ - 2
60
+ - 6
61
+ - 0
62
+ version: 2.6.0
63
+ prerelease: false
64
+ version_requirements: *id003
65
+ name: rspec
66
+ - !ruby/object:Gem::Dependency
67
+ type: :development
68
+ requirement: &id004 !ruby/object:Gem::Requirement
69
+ none: false
70
+ requirements:
71
+ - - ~>
72
+ - !ruby/object:Gem::Version
73
+ hash: 23
74
+ segments:
75
+ - 1
76
+ - 0
77
+ - 0
78
+ version: 1.0.0
79
+ prerelease: false
80
+ version_requirements: *id004
81
+ name: bundler
82
+ - !ruby/object:Gem::Dependency
83
+ type: :development
84
+ requirement: &id005 !ruby/object:Gem::Requirement
85
+ none: false
86
+ requirements:
87
+ - - ~>
88
+ - !ruby/object:Gem::Version
89
+ hash: 11
90
+ segments:
91
+ - 1
92
+ - 6
93
+ - 2
94
+ version: 1.6.2
95
+ prerelease: false
96
+ version_requirements: *id005
97
+ name: jeweler
98
+ - !ruby/object:Gem::Dependency
99
+ type: :development
100
+ requirement: &id006 !ruby/object:Gem::Requirement
101
+ none: false
102
+ requirements:
103
+ - - ">="
104
+ - !ruby/object:Gem::Version
105
+ hash: 3
106
+ segments:
107
+ - 0
108
+ version: "0"
109
+ prerelease: false
110
+ version_requirements: *id006
111
+ name: rcov
112
+ - !ruby/object:Gem::Dependency
113
+ type: :development
114
+ requirement: &id007 !ruby/object:Gem::Requirement
115
+ none: false
116
+ requirements:
117
+ - - ">="
118
+ - !ruby/object:Gem::Version
119
+ hash: 7
120
+ segments:
121
+ - 1
122
+ - 6
123
+ - 4
124
+ version: 1.6.4
125
+ prerelease: false
126
+ version_requirements: *id007
127
+ name: webmock
128
+ description:
129
+ email: lautis@gmail.com
130
+ executables: []
131
+
132
+ extensions: []
133
+
134
+ extra_rdoc_files:
135
+ - LICENSE.txt
136
+ - README.rdoc
137
+ files:
138
+ - .document
139
+ - .rspec
140
+ - Gemfile
141
+ - LICENSE.txt
142
+ - README.rdoc
143
+ - Rakefile
144
+ - VERSION
145
+ - lib/flowdock-git-hook.rb
146
+ - lib/flowdock/git.rb
147
+ - lib/flowdock/git/builder.rb
148
+ - post-receive
149
+ - spec/builder_spec.rb
150
+ - spec/flowdock_git_spec.rb
151
+ - spec/spec_helper.rb
152
+ homepage: http://github.com/flowdock/flowdock-git-hook
153
+ licenses:
154
+ - MIT
155
+ post_install_message:
156
+ rdoc_options: []
157
+
158
+ require_paths:
159
+ - lib
160
+ required_ruby_version: !ruby/object:Gem::Requirement
161
+ none: false
162
+ requirements:
163
+ - - ">="
164
+ - !ruby/object:Gem::Version
165
+ hash: 3
166
+ segments:
167
+ - 0
168
+ version: "0"
169
+ required_rubygems_version: !ruby/object:Gem::Requirement
170
+ none: false
171
+ requirements:
172
+ - - ">="
173
+ - !ruby/object:Gem::Version
174
+ hash: 3
175
+ segments:
176
+ - 0
177
+ version: "0"
178
+ requirements: []
179
+
180
+ rubyforge_project:
181
+ rubygems_version: 1.8.4
182
+ signing_key:
183
+ specification_version: 3
184
+ summary: Git Post-Receive hook for Flowdock
185
+ test_files: []
186
+