redmine_github_hook 2.0.0 → 2.1.0

Sign up to get free protection for your applications and to get access to all the features.
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA1:
3
- metadata.gz: c30863fa492b92934b5a26ac38859b3c914a7521
4
- data.tar.gz: 9f87ea3e8368290abcc8ffe8d6a0e2620189292e
3
+ metadata.gz: 80d3b1fb7dfd0ba831f64fa058e41c143c2a3d85
4
+ data.tar.gz: c1437776bd70f444d30d5c26c4928aa3fef21908
5
5
  SHA512:
6
- metadata.gz: aeb53b9b14fcf449a6d27fc3e7b6f747b0b735605a35f67321687f5f7acdf699dff8e6fb29f39740225a50bd6f21c7ef6bc8ea869c06d2f404547107ea4fe8a0
7
- data.tar.gz: ae96c35e10d754a00acbd373ec0e89eb3875b1c7764b0c25a7f9602b39d92d24e59bbd3ddf66a002615ec945406e856522d1ca652edec8fba8dbc539df32da78
6
+ metadata.gz: d44d1a781d5a714324fe7ec938d8df9f6607783efe6004ebb418a13cad9d56cd1df6f178ad342f276c01bdab51cd8e9928654447d298f657e90780b8f21daf9b
7
+ data.tar.gz: 81d3e3cf4e3aaa09e0837196158d4aa32aec72e620b65b5bf6f909b8aad3a6314631e15d4eff42c927ab564f80b34a8107576e299da1f31a17ba1107f9b46fd9
data/Gemfile CHANGED
@@ -1,4 +1,4 @@
1
1
  source 'https://rubygems.org'
2
2
 
3
- # Specify your gem's dependencies in redmine_github_hook.gemspec
4
- gemspec
3
+ # Use the gems defined in the plugins gemspec
4
+ gemspec(:path => File.dirname(__FILE__))
data/LICENSE CHANGED
@@ -1,4 +1,4 @@
1
- Copyright (c) 2010 Jakob Skjerning
1
+ Copyright (c) 2014 Jakob Skjerning
2
2
 
3
3
  Permission is hereby granted, free of charge, to any person obtaining a copy
4
4
  of this software and associated documentation files (the "Software"), to deal
data/README.md CHANGED
@@ -11,24 +11,46 @@ If your shared repository is on a remote machine - for example on GitHub - this
11
11
  That approach works perfectly fine, but is a bit heavy-handed and cumbersome. The Redmine GitHub Hook plugin allows GitHub to notify your Redmine installation when changes have been pushed to a repository, triggering an update of your local repository and Redmine data only when it is actually necessary.
12
12
 
13
13
 
14
- ## Installation
14
+ ## Getting started
15
15
 
16
- 1. Installing the plugin
17
- 1. Install the [json gem](http://json.rubyforge.org/) on the machine where Redmine is running.
18
- 2. Follow the plugin installation procedure outlined in the [Redmine wiki](http://www.redmine.org/wiki/redmine/Plugins).
19
- * Make sure that Redmine GitHub Hook is installed in a directory named `redmine_github_hook`
20
- 3. Restart your Redmine.
21
- 4. If you already have a local Git repository set up and working from Redmine go to step 3, otherwise continue at step 2.
16
+ ### 1. Install the plugin
22
17
 
23
- 2. Adding a Git repository to a project (note, this should work whether you want to use Redmine GitHub Hook or not). Simply follow the instructions for [keeping your git repository in sync](http://www.redmine.org/wiki/redmine/HowTo_keep_in_sync_your_git_repository_for_redmine).
18
+ You have two options for installing the plugin:
24
19
 
25
- 3. Connecting GitHub to Redmine
26
- 1. Go to the repository Settings interface on GitHub.
27
- 2. Under "Webhooks & Services" add a new "WebHook". The "Payload URL" needs to be of the format: `[redmine_url]/github_hook` (for example `http://redmine.example.com/github_hook`).
28
- 1. By default, GitHub Hook assumes your GitHub repository name is the same as the project identifier in your Redmine installation. If this is not the case, you can specify the actual Redmine project identifier in the Post-Receive URL by using the format `[redmine_url]/github_hook?project_id=[identifier]` (for example `http://redmine.example.com/github_hook?project_id=my_project`).
29
- 2. In most cases, just having the "push" event trigger the webhook should suffice, but you are free to customize the events as you desire.
20
+ #### A: As a RubyGem
30
21
 
31
- That's it. GitHub will now send a HTTP POST to the Redmine GitHub Hook plugin whenever changes are pushed to GitHub. The plugin then takes care of pulling the changes to the local repository and updating the Redmine database with them.
22
+ 1. Add the gem to your Gemfile.local:
23
+ `gem "redmine_github_hook"`
24
+ 2. `bundle`
25
+ 3. Restart your Redmine
26
+
27
+ #### B: As a plugin
28
+
29
+ 1. Follow the plugin installation procedure outlined in the [Redmine wiki](http://www.redmine.org/wiki/redmine/Plugins).
30
+ * Make sure that Redmine GitHub Hook is installed in a directory named `redmine_github_hook`
31
+ * Easiest way: change in your plugins directory and pull with git: `git clone git://github.com/koppen/redmine_github_hook.git`
32
+ 2. Restart your Redmine.
33
+
34
+ ### 2. Add the repository to Redmine
35
+
36
+ Adding a Git repository to a project (note, this should work whether you want to use Redmine GitHub Hook or not).
37
+
38
+ 1. Simply follow the instructions for [keeping your git repository in sync](http://www.redmine.org/wiki/redmine/HowTo_keep_in_sync_your_git_repository_for_redmine).
39
+ * You don't need to set up a cron task as described in the Redmine instructions.
40
+
41
+ ### 3. Connecting GitHub to Redmine
42
+
43
+ 1. Go to the repository Settings interface on GitHub.
44
+ 2. Under "Webhooks & Services" add a new "WebHook". The "Payload URL" needs to be of the format: `[redmine_url]/github_hook` (for example `http://redmine.example.com/github_hook`).
45
+ * By default, GitHub Hook assumes your GitHub repository name is the same as the *project identifier* in your Redmine installation.
46
+ * If this is not the case, you can specify the actual Redmine project identifier in the Post-Receive URL by using the format `[redmine_url]/github_hook?project_id=[identifier]` (for example `http://redmine.example.com/github_hook?project_id=my_project`).
47
+ * GitHub Hook will then update **all repositories** in the specified project. *Be aware, that this process may take a while if you have many repositories in your project.*
48
+ * If you want GitHub Hook to **only update the current repository** you can specify it with an additional parameter in the Post-Receive URL by using the format `[redmine_url]/github_hook?project_id=[identifier]&repository_id=[repository]` (for example `http://redmine.example.com/github_hook?project_id=my_project&repository_id=my_repo`).
49
+ * If you want GitHub Hook to **only update the current repository** you can specify it with an additional parameter in the Post-Receive URL by using the format `[redmine_url]/github_hook?project_id=[identifier]&repository_id=[repository]` (for example `http://redmine.example.com/github_hook?project_id=my_project&repository_id=my_repo`).
50
+ * In most cases, just having the "push" event trigger the webhook should suffice, but you are free to customize the events as you desire.
51
+ * *Note: Make sure you're adding a Webhook - which is what Redmine Github Hook expects. GitHub has some builtin Redmine integration; that's not what you're looking for.*
52
+
53
+ That's it. GitHub will now send a HTTP POST to the Redmine GitHub Hook plugin whenever changes are pushed to GitHub. The plugin then takes care of pulling the changes to the local repositories and updating the Redmine database with them.
32
54
 
33
55
 
34
56
  ## Assumptions
@@ -66,9 +88,18 @@ This means you need to add its SSH keys on GitHub. If the user doesn't already h
66
88
  The user running Redmine needs permissions to read and write to the local repository on the server.
67
89
 
68
90
 
91
+ ## What happens
92
+
93
+ The interactions between the different parts of the process is outlined in the following sequence diagram:
94
+
95
+ ![sequence](https://cloud.githubusercontent.com/assets/6480/3311503/3a789390-f6c5-11e3-804d-d5ca2562799f.png)
96
+
97
+ (Diagram made with [http://bramp.github.io/js-sequence-diagrams/](js-sequence-diagrams)).
98
+
99
+
69
100
  ## License
70
101
 
71
- Copyright (c) 2009-2013 Jakob Skjerning
102
+ Copyright (c) 2009-2014 Jakob Skjerning
72
103
 
73
104
  Permission is hereby granted, free of charge, to any person
74
105
  obtaining a copy of this software and associated documentation
@@ -1,21 +1,14 @@
1
1
  require 'json'
2
2
 
3
3
  class GithubHookController < ApplicationController
4
-
5
- GIT_BIN = Redmine::Configuration['scm_git_command'] || "git"
6
4
  skip_before_filter :verify_authenticity_token, :check_if_login_required
7
5
 
8
6
  def index
9
7
  if request.post?
10
- repositories = find_repositories
11
-
12
- repositories.each do |repository|
13
- # Fetch the changes from Github
14
- update_repository(repository)
15
-
16
- # Fetch the new changesets into Redmine
17
- repository.fetch_changesets
18
- end
8
+ payload = JSON.parse(params[:payload] || '{}')
9
+ updater = GithubHook::Updater.new(payload, params)
10
+ updater.logger = logger
11
+ updater.call
19
12
  end
20
13
 
21
14
  render(:text => 'OK')
@@ -24,94 +17,4 @@ class GithubHookController < ApplicationController
24
17
  def welcome
25
18
  # Render the default layout
26
19
  end
27
-
28
- private
29
-
30
- def system(command)
31
- Kernel.system(command)
32
- end
33
-
34
- # Executes shell command. Returns true if the shell command exits with a
35
- # success status code.
36
- #
37
- # If directory is given the current directory will be changed to that
38
- # directory before executing command.
39
- def exec(command, directory)
40
- logger.debug { "GithubHook: Executing command: '#{command}'" }
41
-
42
- # Get a path to a temp file
43
- logfile = Tempfile.new('github_hook_exec')
44
- logfile.close
45
-
46
- full_command = "#{command} > #{logfile.path} 2>&1"
47
- success = if directory.present?
48
- Dir.chdir(directory) do
49
- system(full_command)
50
- end
51
- else
52
- system(full_command)
53
- end
54
-
55
- output_from_command = File.readlines(logfile.path)
56
- if success
57
- logger.debug { "GithubHook: Command output: #{output_from_command.inspect}"}
58
- else
59
- logger.error { "GithubHook: Command '#{command}' didn't exit properly. Full output: #{output_from_command.inspect}"}
60
- end
61
-
62
- return success
63
- ensure
64
- logfile.unlink
65
- end
66
-
67
- def git_command(command)
68
- GIT_BIN + " #{command}"
69
- end
70
-
71
- # Fetches updates from the remote repository
72
- def update_repository(repository)
73
- command = git_command('fetch origin')
74
- if exec(command, repository.url)
75
- command = git_command("fetch origin \"+refs/heads/*:refs/heads/*\"")
76
- exec(command, repository.url)
77
- end
78
- end
79
-
80
- # Gets the project identifier from the querystring parameters and if that's not supplied, assume
81
- # the Github repository name is the same as the project identifier.
82
- def get_identifier
83
- identifier = get_project_name
84
- raise ActiveRecord::RecordNotFound, "Project identifier not specified" if identifier.nil?
85
- return identifier
86
- end
87
-
88
- # Attempts to find the project name. It first looks in the params, then in the
89
- # payload if params[:project_id] isn't given.
90
- def get_project_name
91
- payload = JSON.parse(params[:payload] || '{}')
92
- params[:project_id] || (payload['repository'] ? payload['repository']['name'] : nil)
93
- end
94
-
95
- # Finds the Redmine project in the database based on the given project identifier
96
- def find_project
97
- identifier = get_identifier
98
- project = Project.find_by_identifier(identifier.downcase)
99
- raise ActiveRecord::RecordNotFound, "No project found with identifier '#{identifier}'" if project.nil?
100
- return project
101
- end
102
-
103
- # Returns the Redmine Repository object we are trying to update
104
- def find_repositories
105
- project = find_project
106
- repositories = project.repositories.select do |repo|
107
- repo.is_a?(Repository::Git)
108
- end
109
-
110
- if repositories.nil? or repositories.length == 0
111
- raise TypeError, "Project '#{project.to_s}' ('#{project.identifier}') has no repository"
112
- end
113
-
114
- return repositories
115
- end
116
-
117
20
  end
@@ -0,0 +1,152 @@
1
+ module GithubHook
2
+ class Updater
3
+ GIT_BIN = Redmine::Configuration['scm_git_command'] || "git"
4
+
5
+ attr_writer :logger
6
+
7
+ def initialize(payload, params = {})
8
+ @payload = payload
9
+ @params = params
10
+ end
11
+
12
+ def call
13
+ repositories = find_repositories
14
+
15
+ repositories.each do |repository|
16
+ tg1 = Time.now
17
+ # Fetch the changes from Github
18
+ update_repository(repository)
19
+ tg2 = Time.now
20
+
21
+ tr1 = Time.now
22
+ # Fetch the new changesets into Redmine
23
+ repository.fetch_changesets
24
+ tr2 = Time.now
25
+
26
+ logger.info { " GithubHook: Redmine repository updated: #{repository.identifier} (Git: #{time_diff_milli(tg1,tg2)}ms, Redmine: #{time_diff_milli(tr1,tr2)}ms)" }
27
+ end
28
+ end
29
+
30
+ private
31
+
32
+ class NullLogger
33
+ def debug(*_); end
34
+ def info(*_); end
35
+ def warn(*_); end
36
+ def error(*_); end
37
+ end
38
+
39
+ attr_reader :params, :payload
40
+
41
+ # Executes shell command. Returns true if the shell command exits with a
42
+ # success status code.
43
+ #
44
+ # If directory is given the current directory will be changed to that
45
+ # directory before executing command.
46
+ def exec(command, directory)
47
+ logger.debug { " GithubHook: Executing command: '#{command}'" }
48
+
49
+ # Get a path to a temp file
50
+ logfile = Tempfile.new('github_hook_exec')
51
+ logfile.close
52
+
53
+ full_command = "#{command} > #{logfile.path} 2>&1"
54
+ success = if directory.present?
55
+ Dir.chdir(directory) do
56
+ system(full_command)
57
+ end
58
+ else
59
+ system(full_command)
60
+ end
61
+
62
+ output_from_command = File.readlines(logfile.path)
63
+ if success
64
+ logger.debug { " GithubHook: Command output: #{output_from_command.inspect}"}
65
+ else
66
+ logger.error { " GithubHook: Command '#{command}' didn't exit properly. Full output: #{output_from_command.inspect}"}
67
+ end
68
+
69
+ return success
70
+ ensure
71
+ logfile.unlink if logfile && logfile.respond_to?(:unlink)
72
+ end
73
+
74
+ # Finds the Redmine project in the database based on the given project identifier
75
+ def find_project
76
+ identifier = get_identifier
77
+ project = Project.find_by_identifier(identifier.downcase)
78
+ raise ActiveRecord::RecordNotFound, "No project found with identifier '#{identifier}'" if project.nil?
79
+ return project
80
+ end
81
+
82
+ # Returns the Redmine Repository object we are trying to update
83
+ def find_repositories
84
+ project = find_project
85
+ repositories = project.repositories.select do |repo|
86
+ repo.is_a?(Repository::Git)
87
+ end
88
+
89
+ if repositories.nil? or repositories.length == 0
90
+ raise TypeError, "Project '#{project.to_s}' ('#{project.identifier}') has no repository"
91
+ end
92
+
93
+ # if a specific repository id is passed in url parameter "repository_id", then try to find it in
94
+ # the list of current project repositories and use only this and not all to pull changes from
95
+ # (issue #54)
96
+ if params.has_key?(:repository_id)
97
+ param_repo = repositories.select do |repo|
98
+ repo.identifier == params[:repository_id]
99
+ end
100
+
101
+ if param_repo.nil? or param_repo.length == 0
102
+ logger.info { " GithubHook: The repository '#{params[:repository_id]}' isn't in the list of projects repos. Updating all repos instead." }
103
+
104
+ else
105
+ repositories = param_repo
106
+ end
107
+ end
108
+
109
+ return repositories
110
+ end
111
+
112
+ # Gets the project identifier from the querystring parameters and if that's not supplied, assume
113
+ # the Github repository name is the same as the project identifier.
114
+ def get_identifier
115
+ identifier = get_project_name
116
+ raise ActiveRecord::RecordNotFound, "Project identifier not specified" if identifier.nil?
117
+ return identifier
118
+ end
119
+
120
+ # Attempts to find the project name. It first looks in the params, then in the
121
+ # payload if params[:project_id] isn't given.
122
+ def get_project_name
123
+ params[:project_id] || (payload['repository'] ? payload['repository']['name'] : nil)
124
+ end
125
+
126
+ def git_command(command)
127
+ GIT_BIN + " #{command}"
128
+ end
129
+
130
+ def logger
131
+ @logger || NullLogger.new
132
+ end
133
+
134
+ def system(command)
135
+ Kernel.system(command)
136
+ end
137
+
138
+ def time_diff_milli(start, finish)
139
+ ((finish - start) * 1000.0).round(1)
140
+ end
141
+
142
+ # Fetches updates from the remote repository
143
+ def update_repository(repository)
144
+ command = git_command('fetch origin')
145
+ if exec(command, repository.url)
146
+ command = git_command("fetch origin \"+refs/heads/*:refs/heads/*\"")
147
+ exec(command, repository.url)
148
+ end
149
+ end
150
+
151
+ end
152
+ end
data/init.rb CHANGED
@@ -4,5 +4,7 @@ Redmine::Plugin.register :redmine_github_hook do
4
4
  name 'Redmine Github Hook plugin'
5
5
  author 'Jakob Skjerning'
6
6
  description 'This plugin allows your Redmine installation to receive Github post-receive notifications'
7
+ url 'https://github.com/koppen/redmine_github_hook'
8
+ author_url 'http://mentalized.net'
7
9
  version RedmineGithubHook::VERSION
8
10
  end
@@ -1,3 +1,3 @@
1
1
  module RedmineGithubHook
2
- VERSION = "2.0.0"
2
+ VERSION = "2.1.0"
3
3
  end
@@ -68,129 +68,35 @@ class GithubHookControllerTest < ActionController::TestCase
68
68
  Project.stubs(:find_by_identifier).with('github').returns(project)
69
69
 
70
70
  # Make sure we don't run actual commands in test
71
- @controller.expects(:system).never
71
+ GithubHook::Updater.any_instance.expects(:system).never
72
72
  Repository.expects(:fetch_changesets).never
73
73
  end
74
74
 
75
- def mock_descriptor(kind, contents = [])
76
- descriptor = mock(kind)
77
- descriptor.expects(:readlines).returns(contents)
78
- descriptor
79
- end
80
-
81
- def do_post(payload = nil)
82
- payload = json if payload.nil?
83
- payload = payload.to_json if payload.is_a?(Hash)
84
- post :index, :payload => payload
85
- end
86
-
87
- def test_should_use_the_repository_name_as_project_identifier
88
- Project.expects(:find_by_identifier).with('github').returns(project)
89
- @controller.stubs(:exec).returns(true)
90
- do_post
91
- end
92
-
93
- def test_should_fetch_changes_from_origin
94
- Project.expects(:find_by_identifier).with('github').returns(project)
95
- @controller.expects(:exec).with("git fetch origin", repository.url)
96
- do_post
97
- end
98
-
99
- def test_should_reset_repository_when_fetch_origin_succeeds
100
- Project.expects(:find_by_identifier).with('github').returns(project)
101
- @controller.expects(:exec).with("git fetch origin", repository.url).returns(true)
102
- @controller.expects(:exec).with("git fetch origin \"+refs/heads/*:refs/heads/*\"", repository.url)
103
- do_post
104
- end
105
-
106
- def test_should_not_reset_repository_when_fetch_origin_fails
107
- Project.expects(:find_by_identifier).with('github').returns(project)
108
- @controller.expects(:exec).with("git fetch origin", repository.url).returns(false)
109
- @controller.expects(:exec).with("git reset --soft refs\/remotes\/origin\/master", repository.url).never
110
- do_post
111
- end
112
-
113
- def test_should_use_project_identifier_from_request
114
- Project.expects(:find_by_identifier).with('redmine').returns(project)
115
- @controller.stubs(:exec).returns(true)
116
- post :index, :project_id => 'redmine', :payload => json
117
- end
118
-
119
- def test_should_return_404_if_project_identifier_not_found
120
- assert_raises ActiveRecord::RecordNotFound do
121
- post :index
122
- end
123
- end
124
-
125
- def test_should_downcase_identifier
126
- # Redmine project identifiers are always downcase
127
- Project.expects(:find_by_identifier).with('redmine').returns(project)
128
- @controller.stubs(:exec).returns(true)
129
- post :index, :project_id => 'ReDmInE', :payload => json
75
+ def do_post
76
+ post :index, :payload => json
130
77
  end
131
78
 
132
79
  def test_should_render_ok_when_done
133
- @controller.expects(:update_repository).returns(true)
134
- do_post
135
- assert_response :success
136
- assert_equal 'OK', @response.body
137
- end
138
-
139
- def test_should_fetch_changesets_into_the_repository
140
- @controller.expects(:update_repository).returns(true)
141
- repository.expects(:fetch_changesets).returns(true)
142
-
80
+ GithubHook::Updater.any_instance.expects(:update_repository).returns(true)
143
81
  do_post
144
82
  assert_response :success
145
83
  assert_equal 'OK', @response.body
146
84
  end
147
85
 
148
- def test_should_return_404_if_project_identifier_not_given
149
- assert_raises ActiveRecord::RecordNotFound do
150
- do_post :repository => {}
151
- end
152
- end
153
-
154
- def test_should_return_404_if_project_not_found
155
- assert_raises ActiveRecord::RecordNotFound do
156
- Project.expects(:find_by_identifier).with('foobar').returns(nil)
157
- do_post :repository => {:name => 'foobar'}
158
- end
159
- end
160
-
161
- def test_should_return_500_if_project_has_no_repository
162
- assert_raises TypeError do
163
- project = mock('project', :to_s => 'My Project', :identifier => 'github')
164
- project.expects(:repositories).returns([])
165
- Project.expects(:find_by_identifier).with('github').returns(project)
166
- do_post :repository => {:name => 'github'}
167
- end
168
- end
169
-
170
- def test_should_return_500_if_repository_is_not_git
171
- assert_raises TypeError do
172
- project = mock('project', :to_s => 'My Project', :identifier => 'github')
173
- repository = Repository::Subversion.new
174
- project.expects(:repositories).at_least(1).returns([repository])
175
- Project.expects(:find_by_identifier).with('github').returns(project)
176
- do_post :repository => {:name => 'github'}
177
- end
178
- end
179
-
180
86
  def test_should_not_require_login
181
- @controller.expects(:update_repository).returns(true)
87
+ GithubHook::Updater.any_instance.expects(:update_repository).returns(true)
182
88
  @controller.expects(:check_if_login_required).never
183
89
  do_post
184
90
  end
185
91
 
186
92
  def test_exec_should_log_output_from_git_as_debug_when_things_go_well
187
- @controller.expects(:system).at_least(1).returns(true)
93
+ GithubHook::Updater.any_instance.expects(:system).at_least(1).returns(true)
188
94
  @controller.logger.expects(:debug).at_least(1)
189
95
  do_post
190
96
  end
191
97
 
192
98
  def test_exec_should_log_output_from_git_as_error_when_things_go_sour
193
- @controller.expects(:system).at_least(1).returns(false)
99
+ GithubHook::Updater.any_instance.expects(:system).at_least(1).returns(false)
194
100
  @controller.logger.expects(:error).at_least(1)
195
101
  do_post
196
102
  end
@@ -0,0 +1,174 @@
1
+ require 'test_helper'
2
+
3
+ require 'test/unit'
4
+ require 'mocha'
5
+
6
+ class GithubHookUpdaterTest < Test::Unit::TestCase
7
+
8
+ def project
9
+ return @project if @project
10
+
11
+ @project ||= Project.new
12
+ @project.repositories << repository
13
+ @project
14
+ end
15
+
16
+ def repository
17
+ return @repository if @repository
18
+
19
+ @repository ||= Repository::Git.new(:identifier => "redmine")
20
+ @repository.stubs(:fetch_changesets).returns(true)
21
+ @repository
22
+ end
23
+
24
+ def payload
25
+ # Ruby hash with the parsed data from the JSON payload
26
+ {"before"=>"5aef35982fb2d34e9d9d4502f6ede1072793222d", "repository"=>{"url"=>"http://github.com/defunkt/github", "name"=>"github", "description"=>"You're lookin' at it.", "watchers"=>5, "forks"=>2, "private"=>1, "owner"=>{"email"=>"chris@ozmm.org", "name"=>"defunkt"}}, "commits"=>[{"id"=>"41a212ee83ca127e3c8cf465891ab7216a705f59", "url"=>"http://github.com/defunkt/github/commit/41a212ee83ca127e3c8cf465891ab7216a705f59", "author"=>{"email"=>"chris@ozmm.org", "name"=>"Chris Wanstrath"}, "message"=>"okay i give in", "timestamp"=>"2008-02-15T14:57:17-08:00", "added"=>["filepath.rb"]}, {"id"=>"de8251ff97ee194a289832576287d6f8ad74e3d0", "url"=>"http://github.com/defunkt/github/commit/de8251ff97ee194a289832576287d6f8ad74e3d0", "author"=>{"email"=>"chris@ozmm.org", "name"=>"Chris Wanstrath"}, "message"=>"update pricing a tad", "timestamp"=>"2008-02-15T14:36:34-08:00"}], "after"=>"de8251ff97ee194a289832576287d6f8ad74e3d0", "ref"=>"refs/heads/master"}
27
+ end
28
+
29
+ def build_updater(payload, options = {})
30
+ updater = GithubHook::Updater.new(payload, options)
31
+ updater.stubs(:exec).returns(true)
32
+ updater
33
+ end
34
+
35
+ def updater
36
+ return @memoized_updater if @memoized_updater
37
+ @memoized_updater = build_updater(payload)
38
+ end
39
+
40
+ def setup
41
+ Project.stubs(:find_by_identifier).with('github').returns(project)
42
+
43
+ # Make sure we don't run actual commands in test
44
+ GithubHook::Updater.any_instance.expects(:system).never
45
+ Repository.expects(:fetch_changesets).never
46
+ end
47
+
48
+ def teardown
49
+ @memoized_updater = nil
50
+ end
51
+
52
+ def test_uses_repository_name_as_project_identifier
53
+ Project.expects(:find_by_identifier).with('github').returns(project)
54
+ updater.call
55
+ end
56
+
57
+ def test_fetches_changes_from_origin
58
+ updater.expects(:exec).with("git fetch origin", repository.url)
59
+ updater.call
60
+ end
61
+
62
+ def test_resets_repository_when_fetch_origin_succeeds
63
+ updater.expects(:exec).with("git fetch origin", repository.url).returns(true)
64
+ updater.expects(:exec).with("git fetch origin \"+refs/heads/*:refs/heads/*\"", repository.url)
65
+ updater.call
66
+ end
67
+
68
+ def test_resets_repository_when_fetch_origin_fails
69
+ updater.expects(:exec).with("git fetch origin", repository.url).returns(false)
70
+ updater.expects(:exec).with("git reset --soft refs\/remotes\/origin\/master", repository.url).never
71
+ updater.call
72
+ end
73
+
74
+ def test_uses_project_identifier_from_request
75
+ Project.expects(:find_by_identifier).with('redmine').returns(project)
76
+ updater = build_updater(payload, {:project_id => 'redmine'})
77
+ updater.call
78
+ end
79
+
80
+ def test_updates_all_repositories_by_default
81
+ another_repository = Repository::Git.new
82
+ another_repository.expects(:fetch_changesets).returns(true)
83
+ project.repositories << another_repository
84
+
85
+ updater = build_updater(payload)
86
+ updater.expects(:exec).with("git fetch origin", repository.url)
87
+ updater.call
88
+ end
89
+
90
+ def test_updates_only_the_specified_repository
91
+ another_repository = Repository::Git.new
92
+ another_repository.expects(:fetch_changesets).never
93
+ project.repositories << another_repository
94
+
95
+ updater = build_updater(payload, {:repository_id => 'redmine'})
96
+ updater.expects(:exec).with("git fetch origin", repository.url)
97
+ updater.call
98
+ end
99
+
100
+ def test_updates_all_repositories_if_specific_repository_is_not_found
101
+ another_repository = Repository::Git.new
102
+ another_repository.expects(:fetch_changesets).returns(true)
103
+ project.repositories << another_repository
104
+
105
+ updater = build_updater(payload, {:repository_id => 'redmine or something'})
106
+ updater.expects(:exec).with("git fetch origin", repository.url)
107
+ updater.call
108
+ end
109
+
110
+ def test_raises_record_not_found_if_project_identifier_not_found
111
+ assert_raises ActiveRecord::RecordNotFound do
112
+ updater = build_updater({})
113
+ updater.call
114
+ end
115
+ end
116
+
117
+ def test_raises_record_not_found_if_project_identifier_not_given
118
+ assert_raises ActiveRecord::RecordNotFound do
119
+ updater = build_updater(payload.merge({"repository" => {}}))
120
+ updater.call
121
+ end
122
+ end
123
+
124
+ def test_raises_record_not_found_if_project_not_found
125
+ assert_raises ActiveRecord::RecordNotFound do
126
+ Project.expects(:find_by_identifier).with('foobar').returns(nil)
127
+ updater = build_updater(payload, {:project_id => "foobar"})
128
+ updater.call
129
+ end
130
+ end
131
+
132
+ def test_downcases_identifier
133
+ # Redmine project identifiers are always downcase
134
+ Project.expects(:find_by_identifier).with('redmine').returns(project)
135
+ updater = build_updater(payload, {:project_id => 'ReDmInE'})
136
+ updater.call
137
+ end
138
+
139
+ def test_fetches_changesets_into_the_repository
140
+ updater.expects(:update_repository).returns(true)
141
+ repository.expects(:fetch_changesets).returns(true)
142
+ updater.call
143
+ end
144
+
145
+ def test_raises_type_error_if_project_has_no_repository
146
+ assert_raises TypeError do
147
+ project = mock('project', :to_s => 'My Project', :identifier => 'github')
148
+ project.expects(:repositories).returns([])
149
+ Project.expects(:find_by_identifier).with('github').returns(project)
150
+ updater.call
151
+ end
152
+ end
153
+
154
+ def test_raises_type_error_if_repository_is_not_git
155
+ assert_raises TypeError do
156
+ project = mock('project', :to_s => 'My Project', :identifier => 'github')
157
+ repository = Repository::Subversion.new
158
+ project.expects(:repositories).at_least(1).returns([repository])
159
+ Project.expects(:find_by_identifier).with('github').returns(project)
160
+ updater.call
161
+ end
162
+ end
163
+
164
+ def test_logs_if_a_logger_is_given
165
+ updater = GithubHook::Updater.new(payload)
166
+ updater.stubs(:exec).returns(true)
167
+
168
+ logger = stub('Logger')
169
+ logger.expects(:info).at_least_once
170
+ updater.logger = logger
171
+
172
+ updater.call
173
+ end
174
+ end
metadata CHANGED
@@ -1,14 +1,14 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: redmine_github_hook
3
3
  version: !ruby/object:Gem::Version
4
- version: 2.0.0
4
+ version: 2.1.0
5
5
  platform: ruby
6
6
  authors:
7
7
  - Jakob Skjerning
8
8
  autorequire:
9
9
  bindir: bin
10
10
  cert_chain: []
11
- date: 2014-04-27 00:00:00.000000000 Z
11
+ date: 2014-08-07 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  name: bundler
@@ -52,6 +52,7 @@ files:
52
52
  - Rakefile
53
53
  - app/controllers/github_hook_controller.rb
54
54
  - app/helpers/git_hook_helper.rb
55
+ - app/services/github_hook/updater.rb
55
56
  - app/views/github_hook/welcome.html.erb
56
57
  - config/routes.rb
57
58
  - init.rb
@@ -61,6 +62,7 @@ files:
61
62
  - redmine_github_hook.gemspec
62
63
  - test/functional/github_hook_controller_test.rb
63
64
  - test/test_helper.rb
65
+ - test/unit/github_hook/updater_test.rb
64
66
  homepage: ''
65
67
  licenses:
66
68
  - MIT
@@ -89,4 +91,4 @@ summary: Allow your Redmine installation to be notified when changes have been p
89
91
  test_files:
90
92
  - test/functional/github_hook_controller_test.rb
91
93
  - test/test_helper.rb
92
- has_rdoc:
94
+ - test/unit/github_hook/updater_test.rb