gitlab-fogbugz 0.0.1 → 0.0.2

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
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