gitlab-fogbugz 0.0.1 → 0.0.2

Sign up to get free protection for your applications and to get access to all the features.
data/README.markdown CHANGED
@@ -51,12 +51,11 @@ Set up your repositories on GitLab to send a post-receive hook to the root url o
51
51
  ### gitlab-fogbugz-server (this app):
52
52
  The configuration file holds several variables that you'll need to edit.
53
53
 
54
- * **fb\_submit\_url**: The url to the cvsSubmit.[php|asp] file on your FogBugz server.
55
54
  * **fb\_main\_url**: The url to your FogBugz's installation.
56
- * **curl**: The path to the curl binary. Curl is used to submit the commit to FogBugz.
57
55
  * **repos**: A list of the SCM repositories that you're using. Each repo has two urls:
58
56
  * *log_url*: The url to the commit log for a specific file
59
57
  * *diff_url*: The url to the specific commit or revision.
58
+ * (Currently unsupported: **fb\_submit\_url**: The url to the cvsSubmit.[php|asp] file on your FogBugz server.)
60
59
 
61
60
  Each repo name must match the the values that are in the *sRepo* field in FogBug's *CVS* table.
62
61
 
data/Rakefile CHANGED
@@ -4,7 +4,7 @@ require "rake/testtask"
4
4
  require "rake/gempackagetask"
5
5
 
6
6
  GEM = "gitlab-fogbugz"
7
- VERSION = "0.0.1"
7
+ VERSION = "0.0.2"
8
8
  AUTHOR = ["John Reilly", "François Beausoleil", "Markus Fischer"]
9
9
  EMAIL = ["jr@trms.com", "francois@teksol.info", "markus@fischer.name"]
10
10
  HOMEPAGE = "http://github.com/mfn/gitlab-fogbugz"
@@ -24,7 +24,10 @@ spec = Gem::Specification.new do |s|
24
24
  s.executables = ["gitlab-fogbugz-server", "gitlab-fogbugz"]
25
25
 
26
26
  # Uncomment this to add a dependency
27
- s.add_dependency "json", "~> 1.1.2"
27
+ s.add_dependency "sinatra", "~> 1.3.2"
28
+ s.add_dependency "json", "~> 1.7.3"
29
+ s.add_dependency "ruby-fogbugz", "~> 0.1.1"
30
+ s.add_development_dependency "mocha"
28
31
 
29
32
  s.require_path = "lib"
30
33
 
data/TODO CHANGED
@@ -1,4 +1,3 @@
1
1
  TODO:
2
- Fix LICENSE with your name
3
- Fix Rakefile with your name and contact info
4
- Add your code to lib/<%= name %>.rb
2
+ - Implement FogBugz checkins once GitLab provides this feature
3
+ (see https://github.com/gitlabhq/gitlabhq/issues/747 )
@@ -5,6 +5,7 @@ require 'sinatra'
5
5
  require 'yaml'
6
6
  require 'cgi'
7
7
  require 'fileutils'
8
+ require 'fogbugz'
8
9
 
9
10
  $: << File.dirname(__FILE__) + "/../lib"
10
11
  require 'fogbugz_service'
@@ -91,15 +92,11 @@ end
91
92
 
92
93
  post "/authenticate" do
93
94
  begin
94
- service = FogbugzService.new(config["fb_main_url"], config["curl"])
95
- service.connect do
96
- token = service.logon(params["email"], params["password"])
97
- tokens[params["email"]] = token
98
- end
99
-
95
+ api = Fogbugz::Interface.new(:email => params["email"], :password => params["password"], :uri => config["fb_main_url"])
96
+ tokens[ params["email"] ] = api.authenticate
100
97
  write_tokens
101
98
  redirect "/authenticated"
102
- rescue FogbugzService::BadCredentials
99
+ rescue Fogbugz::AuthenticationException
103
100
  "<p>Failed authentication: <strong>#{$!.message}</strong></p>" + AUTH_FORM.call(config['fb_main_url'], params)
104
101
  end
105
102
  end
@@ -126,7 +123,7 @@ get "/authenticated" do
126
123
  <li><tt>implements</tt></li>
127
124
  <li><tt>reopens</tt></li>
128
125
  <li><tt>references</tt> or <tt>refs</tt><br>Does not change the state of the case, just adds the commit message to this case.</li>
129
- <li><tt>resolves</tt><br>Resolves is different then <tt>closes</tt>, <tt>completes</tt>, <tt>fixes</tt> and <tt>implements</tt>: it does not explicitly sets the FogBugz case status, thus changes the case to it's categories default status for resolved cases: Bug -&gt; "fixed", Feature -&gt; "Implemented", Inquirt -&gt; "Responded" and Schedule Item -&gt; "Completed"</li>
126
+ <li><tt>resolves</tt><br>Resolves is different then <tt>closes</tt>, <tt>completes</tt>, <tt>fixes</tt> and <tt>implements</tt>: it does not explicitly sets the FogBugz case status, thus changes the case to it's categories default status for resolved cases: Bug -&gt; "fixed", Feature -&gt; "Implemented", Inquiry -&gt; "Responded" and Schedule Item -&gt; "Completed"</li>
130
127
  </ul>
131
128
  <p>NOTE: FogBugz does not allow closing a case that isn't resolved, so you really must use "Implements X, closes X", or else it won't work.</p>
132
129
  <p>Back to <a href="/tokens">Authenticated users</a></p>
@@ -198,7 +195,7 @@ class GitlabFogbugz
198
195
  branch = payload["ref"].split('/').last
199
196
 
200
197
  payload["commits"].each do |c|
201
- process_commit(c["id"], c, repo, branch, payload['before'], config['fb_submit_url'], config['curl'])
198
+ process_commit(c["id"], c, repo, branch, payload['before'])
202
199
  end
203
200
 
204
201
  rescue
@@ -207,16 +204,14 @@ class GitlabFogbugz
207
204
  raise
208
205
  end
209
206
 
210
- def submit_to_fogbugz(sha1, commit, token, curl_path)
207
+ def submit_to_fogbugz(sha1, commit, api)
211
208
  listener = FogbugzListener.new(:commit_url => commit["url"], :message => commit["message"], :sha1 => sha1)
212
209
  MessageParser.parse(commit["message"], listener)
213
- service = FogbugzService.new(config["fb_main_url"], curl_path, token)
214
- service.connect do
215
- listener.update_fogbugz(service)
216
- end
210
+ service = FogbugzService.new(api)
211
+ listener.update_fogbugz(service)
217
212
  end
218
213
 
219
- def process_commit(sha1, commit, repo, branch, before, fb_submit_url, curl_path)
214
+ def process_commit(sha1, commit, repo, branch, before)
220
215
 
221
216
  # from each commit in the payload, we need to extract:
222
217
  # - name of repo, renamed as "gitlab-<repo>"
@@ -229,7 +224,10 @@ class GitlabFogbugz
229
224
  author = commit["author"]["email"]
230
225
 
231
226
  token = tokens[author]
232
- submit_to_fogbugz(sha1, commit, token, curl_path) if token
227
+ raise Exception, "No token available for email '#{author}'" if !token
228
+ api = Fogbugz::Interface.new(:uri => config["fb_main_url"], :token => token)
229
+
230
+ submit_to_fogbugz(sha1, commit, api)
233
231
 
234
232
  files = commit["removed"] | commit["added"] | commit["modified"]
235
233
  files = [] if !files
@@ -247,15 +245,12 @@ class GitlabFogbugz
247
245
  # when fogbugz asks for the scm viewer url.
248
246
  bug_list.each do |fb_bugzid|
249
247
  files.each do |f|
250
- fb_repo = CGI.escape("gitlab-#{repo}")
251
- fb_r1 = CGI.escape("#{before}")
252
- fb_r2 = CGI.escape("#{sha1}")
253
- fb_file = CGI.escape("#{branch}/#{f}")
254
-
255
- #build the GET request, and send it to fogbugz
256
- fb_url = "#{fb_submit_url}?ixBug=#{fb_bugzid}&sRepo=#{fb_repo}&sFile=#{fb_file}&sPrev=#{fb_r1}&sNew=#{fb_r2}"
257
- puts `#{curl_path} --insecure --silent --output /dev/null '#{fb_url}'`
258
-
248
+ # TODO: this is untested code and probably misses the ixRepository
249
+ # parameter; but since gitlab doesn't expose the filenames yet (as of
250
+ # 2.6), can't test
251
+ api.command(:newCheckin, {"ixBug" => fb_bugzid, "sRepo" =>
252
+ "gitlab-#{repo}", "sFile" => "#{branch}/#{f}", "sPrev" =>
253
+ before, "sNew" => sha1} )
259
254
  end
260
255
  end
261
256
  end
@@ -4,14 +4,13 @@
4
4
  # Setup your gitlab-fogbugz integration through this configuration file.
5
5
  #
6
6
  # The URL to the cvsSubmit.[php|asp] file on your server. This is to add the list of changed files to the cases.
7
- fb_submit_url: "http://fogbugz.server.com/cvsSubmit.php"
7
+ # Note: for gitlab-fogbugz this is currently not used; gitlab as of 2.6 lacks
8
+ # the support of file names as part of their post hook
9
+ # fb_submit_url: "http://fogbugz.server.com/cvsSubmit.php"
8
10
 
9
11
  # The URL to the root of your FogBugz installation.
10
12
  fb_main_url: "http://fogbugz.server.com/"
11
13
 
12
- # The path to the Curl binary. Curl is used because of issues with SSL in Ruby's libraries.
13
- curl: "/usr/bin/curl"
14
-
15
14
  # A list of repositories that are known to your FogBugz installation.
16
15
  # Each repository is identified by a name and has 2 values: log_url and diff_url.
17
16
  # The repository's name must match the value given in the sRepos field of your FogBugz installation.
@@ -1,44 +1,9 @@
1
- require "uri"
2
- require "rexml/document"
3
- require "rexml/xpath"
4
- require "cgi"
1
+ require 'fogbugz'
5
2
 
6
3
  class FogbugzService
7
- class FogbugzError < RuntimeError; end
8
- class ClientOutOfDate < FogbugzError; end
9
- class BadXml < FogbugzError; end
10
- class BadCredentials < FogbugzError; end
11
4
 
12
- attr_reader :root_uri, :api_uri
13
-
14
- def initialize(root, curl, token=nil)
15
- @root_uri = root.respond_to?(:scheme) ? root : URI.parse(root)
16
- @curl = curl
17
- @token = token
18
- end
19
-
20
- def validate!
21
- document = get(@root_uri.merge("api.xml"))
22
- raise BadXml, "Did not find the expected root response element. Instead, I found:\n#{document.root}" unless document.root.name == "response"
23
-
24
- minversion = REXML::XPath.first(document.root, "//minversion/text()").to_s
25
- raise ClientOutOfDate, "This client expected to find a minversion <= 3 in the api.xml file. Instead it found #{minversion.inspect}" unless minversion.to_i <= 3
26
-
27
- relative_path = REXML::XPath.first(document.root, "//url/text()")
28
- @api_uri = @root_uri.merge(relative_path.to_s)
29
- end
30
-
31
- def connect
32
- validate!
33
- yield self
34
- end
35
-
36
- def logon(email, password)
37
- params = {"cmd" => "logon", "email" => email, "password" => password}
38
- document = get(@api_uri, params)
39
- bad_logon = REXML::XPath.first(document.root, "//error")
40
- raise BadCredentials, "Bad credentials supplied to Fogbugz: #{bad_logon}" if bad_logon
41
- REXML::XPath.first(document.root, "//token/text()").to_s
5
+ def initialize(api)
6
+ @api = api
42
7
  end
43
8
 
44
9
  def implement(data)
@@ -76,29 +41,10 @@ class FogbugzService
76
41
  end
77
42
 
78
43
  protected
79
- # Returns an REXML::Document to the specified URI
80
- def get(uri, params=nil)
81
- cmd = if params then
82
- query = params.map {|k, v| "#{CGI::escape(k.to_s)}=#{CGI::escape(v.to_s)}"}.join("&")
83
- "#{@curl} --data '#{query}' --silent '#{uri.to_s}'"
84
- else
85
- "#{@curl} --silent '#{uri.to_s}'"
86
- end
87
-
88
- puts cmd
89
- data = `#{cmd}`
90
- begin
91
- REXML::Document.new(data)
92
- rescue REXML::ParseException
93
- raise BadXml, "Could not parse response data:\n#{data}"
94
- end
95
- end
96
-
97
44
  def tell_fogbugz(operation, data, status=nil)
98
- params = {"cmd" => operation.to_s, "ixBug" => data[:case], "sEvent" => data[:message],
99
- "token" => @token}
45
+ params = {"ixBug" => data[:case], "sEvent" => data[:message]}
100
46
  params["ixStatus"] = status if status
101
- get(@api_uri, params)
47
+ @api.command(operation, params)
102
48
  end
103
49
 
104
50
  STATES = {:fixed => 2, :completed => 15, :implemented => 8}
metadata CHANGED
@@ -1,7 +1,7 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: gitlab-fogbugz
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.0.1
4
+ version: 0.0.2
5
5
  prerelease:
6
6
  platform: ruby
7
7
  authors:
@@ -13,6 +13,22 @@ bindir: bin
13
13
  cert_chain: []
14
14
  date: 2012-07-14 00:00:00.000000000 Z
15
15
  dependencies:
16
+ - !ruby/object:Gem::Dependency
17
+ name: sinatra
18
+ requirement: !ruby/object:Gem::Requirement
19
+ none: false
20
+ requirements:
21
+ - - ~>
22
+ - !ruby/object:Gem::Version
23
+ version: 1.3.2
24
+ type: :runtime
25
+ prerelease: false
26
+ version_requirements: !ruby/object:Gem::Requirement
27
+ none: false
28
+ requirements:
29
+ - - ~>
30
+ - !ruby/object:Gem::Version
31
+ version: 1.3.2
16
32
  - !ruby/object:Gem::Dependency
17
33
  name: json
18
34
  requirement: !ruby/object:Gem::Requirement
@@ -20,7 +36,23 @@ dependencies:
20
36
  requirements:
21
37
  - - ~>
22
38
  - !ruby/object:Gem::Version
23
- version: 1.1.2
39
+ version: 1.7.3
40
+ type: :runtime
41
+ prerelease: false
42
+ version_requirements: !ruby/object:Gem::Requirement
43
+ none: false
44
+ requirements:
45
+ - - ~>
46
+ - !ruby/object:Gem::Version
47
+ version: 1.7.3
48
+ - !ruby/object:Gem::Dependency
49
+ name: ruby-fogbugz
50
+ requirement: !ruby/object:Gem::Requirement
51
+ none: false
52
+ requirements:
53
+ - - ~>
54
+ - !ruby/object:Gem::Version
55
+ version: 0.1.1
24
56
  type: :runtime
25
57
  prerelease: false
26
58
  version_requirements: !ruby/object:Gem::Requirement
@@ -28,7 +60,23 @@ dependencies:
28
60
  requirements:
29
61
  - - ~>
30
62
  - !ruby/object:Gem::Version
31
- version: 1.1.2
63
+ version: 0.1.1
64
+ - !ruby/object:Gem::Dependency
65
+ name: mocha
66
+ requirement: !ruby/object:Gem::Requirement
67
+ none: false
68
+ requirements:
69
+ - - ! '>='
70
+ - !ruby/object:Gem::Version
71
+ version: '0'
72
+ type: :development
73
+ prerelease: false
74
+ version_requirements: !ruby/object:Gem::Requirement
75
+ none: false
76
+ requirements:
77
+ - - ! '>='
78
+ - !ruby/object:Gem::Version
79
+ version: '0'
32
80
  description: Fork of github-fogbugz, a gem that acts as the gateway between GitLab
33
81
  and Fogbugz.
34
82
  email:
@@ -52,10 +100,7 @@ files:
52
100
  - lib/message_parser_machine.rb
53
101
  - lib/fogbugz_listener.rb
54
102
  - lib/fogbugz_service.rb
55
- - test/fogbugz_service_logon_test.rb
56
- - test/fogbugz_service_test.rb
57
103
  - test/fogbugz_listener_test.rb
58
- - test/fogbugz_service_case_editing_test.rb
59
104
  - test/message_parser_test.rb
60
105
  - test/test_helper.rb
61
106
  - config/config.yml.example
@@ -1,61 +0,0 @@
1
- require File.dirname(__FILE__) + "/test_helper"
2
- require "fogbugz_service"
3
-
4
- class FogbugzServiceCaseEditingTest < Test::Unit::TestCase
5
- TOKEN = "andf09j"
6
-
7
- def setup
8
- @service_uri = URI.parse("http://fogbugz.my-service.com/")
9
- @service = FogbugzService.new(@service_uri, "/path/to/curl", TOKEN)
10
- @service.stubs(:get).returns(REXML::Document.new(VALID_API_RESPONSE))
11
- @uri = @service.validate!
12
- end
13
-
14
- def test_implement_calls_fogbugz_with_cmd_set_to_resolve_and_status_set_to_implement
15
- params = {"cmd" => "resolve", "ixBug" => "2211", "ixStatus" => FogbugzService::STATES[:implemented],
16
- "sEvent" => "this is the message", "token" => TOKEN}
17
- @service.expects(:get).with(@uri, params).returns(REXML::Document.new(VALID_EDIT_RESPONSE))
18
- @service.implement(:case => "2211", :message => "this is the message")
19
- end
20
-
21
- def test_fix_calls_fogbugz_with_cmd_set_to_resolve_and_status_set_to_fixed
22
- params = {"cmd" => "resolve", "ixBug" => "2211", "ixStatus" => FogbugzService::STATES[:fixed],
23
- "sEvent" => "this is the message", "token" => TOKEN}
24
- @service.expects(:get).with(@uri, params).returns(REXML::Document.new(VALID_EDIT_RESPONSE))
25
- @service.fix(:case => "2211", :message => "this is the message")
26
- end
27
-
28
- def test_complete_calls_fogbugz_with_cmd_set_to_resolve_and_status_set_to_completed
29
- params = {"cmd" => "resolve", "ixBug" => "2211", "ixStatus" => FogbugzService::STATES[:completed],
30
- "sEvent" => "this is the message", "token" => TOKEN}
31
- @service.expects(:get).with(@uri, params).returns(REXML::Document.new(VALID_EDIT_RESPONSE))
32
- @service.complete(:case => "2211", :message => "this is the message")
33
- end
34
-
35
- def test_close_calls_fogbugz_with_cmd_set_to_close
36
- params = {"cmd" => "close", "ixBug" => "2211",
37
- "sEvent" => "this is the message", "token" => TOKEN}
38
- @service.expects(:get).with(@uri, params).returns(REXML::Document.new(VALID_EDIT_RESPONSE))
39
- @service.close(:case => "2211", :message => "this is the message")
40
- end
41
-
42
- def test_append_message_calls_fogbugz_with_cmd_set_to_edit
43
- params = {"cmd" => "edit", "ixBug" => "2211",
44
- "sEvent" => "this is the message", "token" => TOKEN}
45
- @service.expects(:get).with(@uri, params).returns(REXML::Document.new(VALID_EDIT_RESPONSE))
46
- @service.append_message(:case => "2211", :message => "this is the message")
47
- end
48
-
49
- VALID_API_RESPONSE = <<-API
50
- <?xml version="1.0" encoding="UTF-8" ?>
51
- <response>
52
- <version>3</version>
53
- <minversion>1</minversion>
54
- <url>api.asp?</url>
55
- </response>
56
- API
57
-
58
- VALID_EDIT_RESPONSE = <<-API
59
- <?xml version="1.0" encoding="UTF-8" ?>
60
- API
61
- end
@@ -1,47 +0,0 @@
1
- require File.dirname(__FILE__) + "/test_helper"
2
- require "fogbugz_service"
3
-
4
- class FogbugzServiceLogonTest < Test::Unit::TestCase
5
- def setup
6
- @service_uri = URI.parse("http://fogbugz.my-service.com/")
7
- @service = FogbugzService.new(@service_uri, "/path/to/curl")
8
- @service.stubs(:get).returns(REXML::Document.new(VALID_API_RESPONSE))
9
- @uri = @service.validate!
10
- end
11
-
12
- def test_logon_calls_fogbugz_to_retrieve_token
13
- params = {"cmd" => "logon", "email" => "me@my-domain.com", "password" => "my-super-duper-password"}
14
- @service.expects(:get).with(@uri, params).returns(REXML::Document.new(VALID_LOGON_RESPONSE))
15
- @service.logon("me@my-domain.com", "my-super-duper-password")
16
- end
17
-
18
- def test_logon_returns_token
19
- @service.stubs(:get).returns(REXML::Document.new(VALID_LOGON_RESPONSE))
20
- assert_equal "24dsg34lok43un23", @service.logon("me@my-domain.com", "my-super-duper-password")
21
- end
22
-
23
- def test_logon_raises_bad_credentials_when_logon_fails
24
- @service.stubs(:get).returns(REXML::Document.new(FAILED_LOGON_RESPONSE))
25
- assert_raise FogbugzService::BadCredentials do
26
- @service.logon("me@my-domain.com", "my-super-duper-password")
27
- end
28
- end
29
-
30
- VALID_API_RESPONSE = <<-API
31
- <?xml version="1.0" encoding="UTF-8" ?>
32
- <response>
33
- <version>3</version>
34
- <minversion>1</minversion>
35
- <url>api.asp?</url>
36
- </response>
37
- API
38
-
39
- VALID_LOGON_RESPONSE = <<-API
40
- <?xml version="1.0" encoding="UTF-8" ?>
41
- <response><token>24dsg34lok43un23</token></response>
42
- API
43
-
44
- FAILED_LOGON_RESPONSE = <<-API
45
- <response><error code="1">Error Message To Show User</error></response>
46
- API
47
- end
@@ -1,83 +0,0 @@
1
- require File.dirname(__FILE__) + "/test_helper"
2
- require "fogbugz_service"
3
-
4
- class FogbugzServiceTest < Test::Unit::TestCase
5
- def setup
6
- @service_uri = URI.parse("http://fogbugz.my-service.com/")
7
- @service = FogbugzService.new(@service_uri, "/path/to/curl")
8
- end
9
-
10
- def test_validate_connects_to_fogbugz_and_retrieves_the_api_url
11
- @service.expects(:get).with(@service_uri.merge("api.xml")).returns(REXML::Document.new(VALID_API_RESPONSE))
12
- @service.validate!
13
- end
14
-
15
- def test_validate_parses_response_to_find_url
16
- @service.stubs(:get).returns(REXML::Document.new(VALID_API_RESPONSE))
17
- @service.validate!
18
- assert_equal @service_uri.merge("api.asp?"), @service.api_uri
19
- end
20
-
21
- def test_validate_returns_the_api_uri
22
- @service.stubs(:get).returns(REXML::Document.new(VALID_API_RESPONSE))
23
- assert_equal @service_uri.merge("api.asp?"), @service.validate!
24
- end
25
-
26
- def test_validate_raises_if_minimum_version_is_not_one
27
- @service.stubs(:get).returns(REXML::Document.new(RECENT_API_RESPONSE))
28
- assert_raise(FogbugzService::ClientOutOfDate) do
29
- @service.validate!
30
- end
31
- end
32
-
33
- def test_validate_raises_if_xhtml_returned
34
- @service.stubs(:get).returns(REXML::Document.new(VALID_XHTML_RESPONSE))
35
- assert_raise(FogbugzService::BadXml) do
36
- @service.validate!
37
- end
38
- end
39
-
40
- def test_validate_does_validation_only_once
41
- @service.expects(:get).once.returns(REXML::Document.new(VALID_API_RESPONSE))
42
- @service.validate!
43
- end
44
-
45
- def test_connect_calls_validate_and_yields
46
- @service.expects(:validate!)
47
- yielded = false
48
- @service.connect do |service|
49
- assert_equal service, @service
50
- yielded = true
51
- end
52
-
53
- assert yielded
54
- end
55
-
56
- VALID_API_RESPONSE = <<-API
57
- <?xml version="1.0" encoding="UTF-8" ?>
58
- <response>
59
- <version>3</version>
60
- <minversion>1</minversion>
61
- <url>api.asp?</url>
62
- </response>
63
- API
64
-
65
- RECENT_API_RESPONSE = <<-API
66
- <?xml version="1.0" encoding="UTF-8" ?>
67
- <response>
68
- <version>9</version>
69
- <minversion>7</minversion>
70
- <url>api.asp?</url>
71
- </response>
72
- API
73
-
74
- VALID_XHTML_RESPONSE = <<-API
75
- <html>
76
- <head>
77
- <title>Fogbugz API</title>
78
- </head>
79
- <body>
80
- </body>
81
- </html>
82
- API
83
- end