Include GitHub events in the new Basecamp timeline #303

Merged
merged 2 commits into from Apr 27, 2012
View
@@ -4,24 +4,18 @@ Basecamp
Install Notes
-------------
- 1. url should be your Basecamp url
- 2. username should be the username or API token of the user that you want to use to post messages into your Basecamp - you can setup a user just for this purpose
- 3. password should be the password of the user that you want to use to post the messages. If username is an API token, set password to 'x'.
- 4. project should be the name of the project that you want to post the message into (not the id)
- 5. category should be the name of the category that you want to post the message using (not the id)
- 6. ssl should be enabled for accounts that need SSL.
+ 1. project_url is the URL of your Basecamp project: https://basecamp.com/1234/projects/5678
+ 2. email_address is the email you sign in to Basecamp with. This person must have access to the project. To add events on behalf of other people, make the person an admin on the project.
+ 3. password is the password you sign in to Basecamp with.
Developer Notes
---------------
data
- - url
- - username
+ - project_url
+ - email_address
- password
- - project
- - category
- - ssl
payload
- - refer to docs/github_payload
+ - refer to docs/github_payload
View
@@ -0,0 +1,27 @@
+Basecamp Classic
+================
+
+Install Notes
+-------------
+
+ 1. url should be your Basecamp Classic url
+ 2. username should be the username or API token of the user that you want to use to post messages into your Basecamp - you can setup a user just for this purpose
+ 3. password should be the password of the user that you want to use to post the messages. If username is an API token, set password to 'x'.
+ 4. project should be the name of the project that you want to post the message into (not the id)
+ 5. category should be the name of the category that you want to post the message using (not the id)
+ 6. ssl should be enabled for accounts that need SSL.
+
+
+Developer Notes
+---------------
+
+data
+ - url
+ - username
+ - password
+ - project
+ - category
+ - ssl
+
+payload
+ - refer to docs/github_payload
View
@@ -1,143 +1,71 @@
class Service::Basecamp < Service
- string :url, :project, :category, :username
- password :password
- boolean :ssl
+ string :project_url, :email_address
+ password :password
+ white_list :project_url, :email_address
+ default_events :push, :pull_request, :issues
- white_list :url, :project, :category, :username
- def receive_push
- raise_config_error "Invalid basecamp domain" if basecamp_domain.nil?
-
- repository = payload['repository']['name']
- name_with_owner = File.join(payload['repository']['owner']['name'], repository)
- branch = ref_name
-
- commits = payload['commits'].reject { |commit| commit['message'].to_s.strip == '' }
- return if commits.empty?
-
- ::Basecamp.establish_connection! basecamp_domain,
- data['username'], data['password'], data['ssl'].to_i == 1
-
- commits.each do |commit|
- gitsha = commit['id']
- short_git_sha = gitsha[0..5]
- timestamp = Date.parse(commit['timestamp'])
-
- added = commit['added'].map { |f| ['A', f] }
- removed = commit['removed'].map { |f| ['R', f] }
- modified = commit['modified'].map { |f| ['M', f] }
- changed_paths = (added + removed + modified).sort_by { |(char, file)| file }
- changed_paths = changed_paths.collect { |entry| entry * ' ' }.join("\n ")
-
- # Shorten the elements of the subject
- commit_title = commit['message'][/^([^\n]+)/, 1]
- if commit_title.length > 50
- commit_title = commit_title.slice(0,50) << '...'
- end
-
- title = "Commit on #{name_with_owner}: #{short_git_sha}: #{commit_title}"
-
- body = <<-EOH
-*Author:* #{commit['author']['name']} <#{commit['author']['email']}>
-*Commit:* <a href="#{commit['url']}">#{gitsha}</a>
-*Date:* #{timestamp} (#{timestamp.strftime('%a, %d %b %Y')})
-*Branch:* #{branch}
-*Home:* #{payload['repository']['url']}
-
-h2. Log Message
-
-<pre>#{commit['message']}</pre>
-EOH
-
- if changed_paths.size > 0
- body << <<-EOH
-
-h2. Changed paths
-
-<pre> #{changed_paths}</pre>
-EOH
- end
+ def hook_name
+ 'bcx'
+ end
- post_message :title => title, :body => body
- end
+ def receive_push
+ commit = payload['commits'].last || {}
+ author = commit['author'] || commit['committer'] || payload['pusher']
- rescue SocketError => boom
- if boom.to_s =~ /getaddrinfo: Name or service not known/
- raise_config_error "Invalid basecamp domain name"
- else
- raise
- end
- rescue ActiveResource::UnauthorizedAccess => boom
- raise_config_error "Unauthorized. Verify the project URL and credentials."
- rescue ActiveResource::ForbiddenAccess => boom
- raise_config_error boom.to_s
- rescue ActiveResource::Redirection => boom
- raise_config_error "Invalid project URL: #{boom}"
- rescue RuntimeError => boom
- if boom.to_s =~ /\((?:403|401|422)\)/
- raise_config_error "Invalid credentials: #{boom}"
- elsif boom.to_s =~ /\((?:404|301)\)/
- raise_config_error "Invalid project URL: #{boom}"
- elsif boom.to_s == 'Unprocessable Entity (422)'
- # do nothing
- else
- raise
- end
+ message = summary_message.sub("[#{repo_name}] #{pusher_name} ", '')
+ create_event 'committed', message, summary_url, author['email']
end
- attr_writer :basecamp
- attr_writer :project_id
- attr_writer :category_id
+ def receive_pull_request
+ base_ref = pull.base.label.split(':').last
+ head_ref = pull.head.label.split(':').last
+ head_ref = pull.head.label if head_ref == base_ref
- def basecamp_domain
- @basecamp_domain ||= Addressable::URI.parse(data['url'].to_s).host
- rescue Addressable::URI::InvalidURIError
+ create_event "#{action} a pull request",
+ "#{pull.title} (#{base_ref}..#{head_ref})",
+ pull.html_url
end
- def build_message(options = {})
- m = ::Basecamp::Message.new :project_id => project_id
- m.category_id = category_id
- options.each do |key, value|
- m.send "#{key}=", value
- end
- m
+ def receive_issues
+ create_event "#{action} an issue", issue.title, issue.html_url
end
- def post_message(options = {})
- build_message(options).save
- end
- def all_projects
- Array(::Basecamp::Project.all)
- end
+ private
- def all_categories
- Array(::Basecamp::Category.post_categories(project_id))
+ def create_event(action, message, url, author_email = nil)
+ http_post_event :service => 'GitHub',
+ :creator_email_address => author_email,
+ :description => action,
+ :title => message,
+ :url => url
end
- def project_id
- @project_id ||= begin
- name = data['project'].to_s
- name.downcase!
- projects = all_projects.select { |p| p.name.downcase == name }
- case projects.size
- when 1 then projects.first.id
- when 0 then raise_config_error("Invalid Project: #{name.downcase}")
- else raise_config_error("Multiple projects named: #{name.downcase}")
- end
+ def http_post_event(params)
+ http.basic_auth data['email_address'], data['password']
+ http.headers['User-Agent'] = 'GitHub service hook'
+ http.headers['Content-Type'] = 'application/json'
+ http.headers['Accept'] = 'application/json'
+
+ response = http_post(events_api_url, params.to_json)
+
+ case response.status
+ when 401; raise_config_error "Invalid email + password: #{response.body.inspect}"
+ when 403; raise_config_error "No access to project: #{response.body.inspect}"
+ when 404; raise_config_error "No such project: #{response.body.inspect}"
+ when 422; raise_config_error "Validation error: #{response.body.inspect}"
end
end
- def category_id
- @category_id ||= begin
- name = data['category'].to_s
- name.downcase!
- categories = all_categories.select { |c| c.name.downcase == name }
- case categories.size
- when 1 then categories.first.id
- when 0 then raise_config_error("Invalid Category: #{name.downcase}")
- else raise_config_error("Multiple categories named: #{name.downcase}")
- end
+ EVENTS_API_URL = 'https://basecamp.com:443/%d/api/v1/projects/%d/events.json'
+ def events_api_url
+ if data['project_url'] =~ %r{^https://basecamp\.com/(\d+)/projects/(\d+)}
+ EVENTS_API_URL % [$1, $2]
+ elsif data['project_url'] =~ /basecamphq\.com/
+ raise_config_error "That's a URL for a Basecamp Classic project, not the new Basecamp. Check out the Basecamp Classic service hook instead!"
+ else
+ raise_config_error "That's not a URL to a Basecamp project! Navigate to the Basecamp project you'd like to post to and note the URL. It should look something like: https://basecamp.com/123456/projects/7890123 -- paste that URL here."
end
end
end
Oops, something went wrong.