Permalink
Browse files

Initial commit

  • Loading branch information...
0 parents commit 1b94eeabdd3f026e09b3df2a8a0af0c8e8134389 John Reilly committed Apr 24, 2008
Showing with 221 additions and 0 deletions.
  1. +1 −0 .gitignore
  2. +57 −0 README.markdown
  3. +12 −0 config.yml.example
  4. +106 −0 github-fogbugz.rb
  5. +12 −0 test-fogbugz-url.rb
  6. +33 −0 test-receive-commit.rb
@@ -0,0 +1 @@
+config.yml
@@ -0,0 +1,57 @@
+GitHub + Fogbugz
+===
+
+This is a simple sinatra application that has two responsibilities:
+
+* Receive and parse the JSON commit info from GitHub's post-receive hooks and send it to FogBugz
+* Act as a "gateway" for viewing multiple SCM repositories in FogBugz.
+
+To Install and Run:
+---
+
+ $ sudo gem install sinatra json
+ $ mv config.yml.example config.yml
+ $ ...edit config.yml (see below)...
+ $ ruby github-fogbugz.rb [-p <port>]
+
+Configuration
+---
+
+### GitHub repositories:
+Set up your repositories on GitHub to send a [post-receive hook](http://github.com/guides/post-receive-hooks) to the root url of this sinatra app. Be sure to include the port, if other than 80.
+
+### github-fogbugz (this app):
+The configuration file holds several variables that you'll need to edit.
+
+* **fb\_submit\_url**: The url to the cvsSubmit.[php|asp] file on your FogBugz server.
+* **curl**: The path to the curl binary. Curl is used to submit the commit to FogBugz.
+* **repos**: A list of the SCM repositories that you're using. Each repo has two urls:
+ * *log_url*: The url to the commit log for a specific file
+ * *diff_url*: The url to the specific commit or revision.
+
+Each repo name must match the the values that are in the *sRepo* field in FogBug's *CVS* table.
+
+### FogBugz:
+You'll need to do some configuration in FogBugz as well. As the FogBugz admin, edit your site settings, and in the source control urls for logs and diffs, enter:
+
+* Logs: "http://thisapp:port/repo_url?type=log&repo=^REPO&file=^FILE&r1=^R1&r2=^R2"
+* Diffs: "http://thisapp:port/repo_url?type=diff&repo=^REPO&file=^FILE&r1=^R1&r2=^R2"
+
+The only difference between the two is the "type" parameter.
+
+> I'm not a fan of Fog Creek's [suggested solution](http://www.fogcreek.com/FogBugz/KB/howto/MultipleRepositories-Mult.html) for multiple repositories, as it requires you to copy a new script into the FogBugz website directory. This seems fine, but (as is my understanding) you'll have to copy it over again and again with each FogBugz upgrade because the website directory gets recreated each time. That's why this script also acts as the SCM viewer "gateway."
+
+**Note:** If you've been using FogBugz in the past with only a single repository, odds are your *sRepo* field is empty. Mine was. Be sure that all of the records in FogBugz's *CVS* table have a valid *sRepo* that matches up to a repo specified in the config file.
+
+Other Notes
+---
+When parsing out the file names from github commits, I've tacked on the branch that the file lives on. So in FogBugz you'll see files like "master/myfile.rb". This is simply because my team does the "release on a branch" thing (aka [Release Line](http://www.scmpatterns.com/book/pattern-summary.html)), and I like to see which branch certain bugs were fixed on. Feel free to modify this behavior.
+
+Caveats
+---
+It's fairly obvious that FogBugz was written for a more traditional CVS/SVN SCM system in mind. As such, the commit list display doesn't really jive with git:
+
+![Messy Commits List in FogBugz](http://img.skitch.com/20080424-kb6kujbfd224436pqgnhgj33sk.jpg)
+
+This is in FogBugz 6.1.23. I've got a [thread started](http://support.fogcreek.com/default.asp?fogbugz.4.24526.0) on their forum asking for this to be cleaned up a bit. We'll see if it gets better in future releases.
+
@@ -0,0 +1,12 @@
+fb_submit_url: "http://fogbugz.server.com/cvsSubmit.php"
+curl: "/usr/bin/curl"
+repos:
+ cvs:
+ log_url: "http://cvs.server.com/viewcvs.cgi/^FILE?rev=1.1&view=log"
+ diff_url: "http://cvs.trms.com/viewcvs.cgi/^FILE?r1=^R1&r2=^R2"
+ github-fogbugz:
+ log_url: "https://github.com/johnreilly/github-fogbugz/commits/^FILE"
+ diff_url: "https://github.com/johnreilly/github-fogbugz/commit/^R2"
+ github-myotherproject:
+ log_url: "https://github.com/username/github-myotherproject/commits/^FILE"
+ diff_url: "https://github.com/username/github-myotherproject/commit/^R2"
@@ -0,0 +1,106 @@
+require 'rubygems'
+require 'json'
+require 'sinatra'
+require 'yaml'
+require 'cgi'
+
+# This sinatra app has two endpoints:
+# / This is where GitHub will send
+# its post-receive hooks
+#
+# /repo_url This is where FogBugz will send
+# you when you click on a commit link
+
+
+##
+# GitHub should send its post-receive hook here.
+post '/' do
+ GithubFogbugz.new(params[:payload])
+end
+
+##
+# Set the log and diff urls (in fogbugz's site settings) to point here.
+# Log url: http://localhost:4567/repo_url?type=log&repo=^REPO&file=^FILE&r1=^R1&r2=^R2
+# Diff url: http://localhost:4567/repo_url?type=diff&repo=^REPO&file=^FILE&r1=^R1&r2=^R2
+get '/repo_url' do
+ config = YAML.load_file('config.yml')
+
+ #pull out the repo's scm viewer url from the config file
+ if params[:type] == 'log'
+ url = config['repos'][params[:repo]]['log_url']
+ elsif params[:type] == 'diff'
+ url = config['repos'][params[:repo]]['diff_url']
+ else
+ "Unknown repo viewer type."
+ end
+
+ if url
+ url.gsub!(/\^REPO/, params[:repo])
+ url.gsub!(/\^FILE/, params[:file])
+ url.gsub!(/\^R1/, params[:r1])
+ url.gsub!(/\^R2/, params[:r2])
+ redirect url
+ end
+
+end
+
+
+
+
+##
+# This class does all of the json parsing and submits a push's commits to fogbugz
+class GithubFogbugz
+
+ def initialize(payload)
+ config = YAML.load_file('config.yml')
+
+ payload = JSON.parse(payload)
+ return unless payload.keys.include?("repository")
+
+ repo = payload["repository"]["name"]
+ branch = payload["ref"].split('/').last
+
+ payload["commits"].each do |c|
+ process_commit(c.first, c.last, repo, branch, before, config['fb_submit_url'], config['curl'])
+ end
+
+ end
+
+ def process_commit(sha1, commit, repo, branch, before, fb_submit_url, curl_path)
+
+ # from each commit in the payload, we need to extract:
+ # - name of repo, renamed as "github-<repo>"
+ # - name of file, including branch. e.g.: "4.7/Builds/Cablecast.fbp4"
+ # - sha1 of commit (R2)
+ # - sha1 of before (R1)
+ # - bugzid (found inside the commit message)
+
+ message = commit["message"]
+ files = commit["removed"] | commit["added"] | commit["modified"]
+
+ # look for a bug id in each line of the commit message
+ bug_list = []
+ message.split("\n").each do |line|
+ if (line =~ /\s*Bug[zs]*\s*IDs*\s*[#:; ]+((\d+[ ,:;#]*)+)/i)
+ bug_list << $1.to_i
+ end
+ end
+
+ # for each found bugzid, submit the files to fogbugz.
+ # this will set the sRepo to "github-<repo>", which will be used above
+ # when fogbugz asks for the scm viewer url.
+ bug_list.each do |fb_bugzid|
+ files.each do |f|
+ fb_repo = CGI.escape("github-#{repo}")
+ fb_r1 = CGI.escape("#{before}")
+ fb_r2 = CGI.escape("#{sha1}")
+ fb_file = CGI.escape("#{branch}/#{f}")
+
+ #build the GET request, and send it to fogbugz
+ fb_url = "#{fb_submit_url}?ixBug=#{fb_bugzid}&sRepo=#{fb_repo}&sFile=#{fb_file}&sPrev=#{fb_r1}&sNew=#{fb_r2}"
+ puts `#{curl_path} --insecure --silent --output /dev/null '#{fb_url}'`
+
+ end
+ end
+ end
+end
@@ -0,0 +1,12 @@
+require 'net/http'
+require 'cgi'
+
+
+url = URI.parse('http://localhost:4567/repo_url')
+req = Net::HTTP::Get.new(url.path)
+req.set_form_data(:type => "diff", :repo => "cvs", :file => "filename.rb", :r1 => "123", :r2 => "124")
+res = Net::HTTP.new(url.host, url.port).start {|http| http.request(req) }
+
+
+req.set_form_data(:type => "log", :repo => "github-fbtest", :file => "4.7/filename.rb", :r1 => "123", :r2 => "124")
+res = Net::HTTP.new(url.host, url.port).start {|http| http.request(req) }
@@ -0,0 +1,33 @@
+require 'net/http'
+
+payload = %Q{
+ {"commits":
+ { "a72c48d7335486b50b29ef31bb3f694febb9cc62":
+ { "removed":["ghook.rb"],
+ "author":{"name":"John Reilly","email":"jr@trms.com"},
+ "added":[".gitignore","config.yml.example","github-fogbugz.rb"],
+ "timestamp":"2008-04-23T12:46:10-07:00",
+ "modified":[],
+ "message":"bugzid: 3083 slight refactoring",
+ "url":"http:\/\/github.com\/johnreilly\/fbtest\/commit\/a72c48d7335486b50b29ef31bb3f694febb9cc62"},
+ "ae0b864ad73325c4e8f5c195cd9a9a33cd73e46b":
+ { "removed":[],
+ "author":{"name":"John Reilly","email":"jr@trms.com"},
+ "added":["github-fogbugz-test.rb"],
+ "timestamp":"2008-04-23T13:30:12-07:00",
+ "modified":["github-fogbugz.rb"],
+ "message":"bugzid: 3083 adding some tests",
+ "url":"http:\/\/github.com\/johnreilly\/fbtest\/commit\/ae0b864ad73325c4e8f5c195cd9a9a33cd73e46b"}},
+ "after":"ae0b864ad73325c4e8f5c195cd9a9a33cd73e46b",
+ "before":"6254706d8facde1191f2a96e27200f6057ccc14e",
+ "ref":"refs\/heads\/master",
+ "repository":
+ { "name":"fbtest",
+ "owner":{"name":"johnreilly","email":"jr@trms.com"},
+ "url":"http:\/\/github.com\/johnreilly\/fbtest"}}}
+
+url = URI.parse('http://localhost:4567/')
+req = Net::HTTP::Post.new(url.path)
+req.set_form_data({'payload' => payload})
+res = Net::HTTP.new(url.host, url.port).start {|http| http.request(req) }
+puts res

0 comments on commit 1b94eea

Please sign in to comment.