forked from github/github-services
-
Notifications
You must be signed in to change notification settings - Fork 1
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
Include GitHub on the new Basecamp timeline.
Rename the old Basecamp hook to Basecamp Classic, but keep the internal name for back-compat.
- Loading branch information
Showing
6 changed files
with
333 additions
and
150 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -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 |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -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.