solicit 0.1.0 → 0.1.1

Sign up to get free protection for your applications and to get access to all the features.
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: 115b96f84a7bc69ebfabf45139d2261663e73f2b0895d0ce8456a055c124f7af
4
- data.tar.gz: 736dca15502cdfad1c6218d083a0567938e11cf4bdc29d02c79c413b790f5625
3
+ metadata.gz: 239ba565f4a101f8d7e705663bba9e001c614854c40c24c4ed7ff9bf9da05896
4
+ data.tar.gz: 58a72e1639c8a440dedc7eed64d3bdf247017389505bf22992a26ddf1aefbe45
5
5
  SHA512:
6
- metadata.gz: 0ddb5c194e2a18cb5f05b7f26817decfe1bb9f47d6d88683df6166c866e9beab6ed1919761324c41f0b63de50224bcd27c3c722d81a2346e3890484e3c208a63
7
- data.tar.gz: 9f07848217775d9de5713802b436e3f310a3f32348f2469645a4438c6d415bc1e20539868105b70ac8ad748f063e87b8aae81570736a3452ee8aba8bcbb7bd04
6
+ metadata.gz: 9f87726122bfcf127d144098f10bf5a3e617dd03ce242eefa375bc9ca6ec19b2c40c995e90ff654db5a26f4ff52b18c60ad9bf78a547fe1f7fec367c3e4140a2
7
+ data.tar.gz: bc47411a5692d44c2c0f1f0eeb8549c548258383773664dac274488d4547532700f3594c0d57543b9175b4962ab7752463ab89d2c635add48538986daa833ddd
data/README.md CHANGED
@@ -1,35 +1,118 @@
1
1
  # Solicit
2
2
 
3
- Welcome to your new gem! In this directory, you'll find the files you need to be able to package up your Ruby library into a gem. Put your Ruby code in the file `lib/solicit`. To experiment with that code, run `bin/console` for an interactive prompt.
3
+ Solicit requests reviewers from your slack channel automatically!
4
4
 
5
- TODO: Delete this and the text above, and describe your gem
5
+ It works by listening for labels on your repo(s) from the github api,
6
+ and posting a message into a specified slack channel which allows people
7
+ to assign themselves to the pull request.
8
+
9
+ This was developed at the third annual Optimal Workshop hackathon.
6
10
 
7
11
  ## Installation
8
12
 
9
- Add this line to your application's Gemfile:
13
+ 1. Add an outgoing webhook to your github repository, with the 'pull requests' event type enabled
14
+
15
+ ![](/images/step1.png)
16
+ _NB: the 'Secret' value here will become your github_secret value_
10
17
 
18
+ 2. [Create a slack app] for your team
19
+ ![](/images/step2.png)
20
+ _NB: note your 'Webhook URL' and 'Verification token' here to use as 'slack_webhook_url and slack_secret later_
21
+
22
+ 3. Configure the gem on your app
11
23
  ```ruby
24
+ # Gemfile
12
25
  gem 'solicit'
13
26
  ```
27
+ ```ruby
28
+ # routes.rb
29
+ mount Solicit::Engine, at: '/solicit'
30
+ ```
31
+ ```ruby
32
+ # app/initializers/solicit.rb
33
+
34
+ # required
35
+ Solicit.slack_webhook_url = "<your webhook url>"
36
+ Solicit.github_api_token = "<your api token>"
37
+ Solicit.contributors_map = {
38
+ timmy: { handle: "TimmyTeaco", name: "Timmy", emoji: "dolphin" }
39
+ jimmy: { handle: "JimmyBoy", name: "Jimmy", emoji: "whale" }
40
+ }
41
+
42
+ # optional
43
+ Solicit.labels_map = { "review me": "#reviewers" } # default is { "go" => "#general" }
44
+ Solicit.slack_username = "ReviewBot"
45
+ Solicit.slack_secret = "<your slack secret>"
46
+ Solicit.github_secret = "<your github secret>"
47
+ Solicit.needs_more_assignees = proc do |github_response, label|
48
+ label == "swarm"
49
+ end
50
+ ```
51
+
52
+ More info on each option is below:
53
+
54
+ ##### slack_webhook_url (required)
55
+
56
+ The url of the webhook provided by your slack app
57
+
58
+ https://api.slack.com/incoming-webhooks
59
+
60
+ ##### github_api_token (required)
61
+
62
+ A personal API token with access to your github repo.
14
63
 
15
- And then execute:
64
+ https://github.blog/2013-05-16-personal-api-tokens/
16
65
 
17
- $ bundle
66
+ ##### contributors_map (required)
18
67
 
19
- Or install it yourself as:
68
+ A hash which maps slack usernames to github usernames.
69
+ You can optionally provide a custom emoji which will appear when this
70
+ person claims a PR.
20
71
 
21
- $ gem install solicit
72
+ ##### labels_map (optional)
22
73
 
23
- ## Usage
74
+ A hash which maps github label names to the slack channel in which they should appear. By default, any PR that has the 'go' label placed on it will notify the #general channel.
24
75
 
25
- TODO: Write usage instructions here
76
+ ##### slack_username (optional)
77
+
78
+ The username which should appear as the message sender. By default, the bot is named 'Solicit'
79
+
80
+ ##### slack_secret (optional)
81
+
82
+ A signing token from slack to secure requests to your app.
83
+ You can read more about setting up secure slack webhooks here:
84
+
85
+ https://api.slack.com/docs/verifying-requests-from-slack
86
+
87
+ ##### github_secret (optional)
88
+
89
+ A signing token from github to secure requests to your app.
90
+ You can read more about setting up secure github webhooks here:
91
+
92
+ https://developer.github.com/webhooks/securing/
93
+
94
+ ##### needs_more_assignees (optional)
95
+
96
+ Some organizations may have rules around how many assignees should be
97
+ put on a given PR. If so, you can define a method which returns whether
98
+ a PR should continue to accept reviewers.
99
+ (TODO explain this better)
26
100
 
27
101
  ## Development
28
102
 
29
- After checking out the repo, run `bin/setup` to install dependencies. You can also run `bin/console` for an interactive prompt that will allow you to experiment.
103
+ #### Installation
30
104
 
31
105
  To install this gem onto your local machine, run `bundle exec rake install`. To release a new version, update the version number in `version.rb`, and then run `bundle exec rake release`, which will create a git tag for the version, push git commits and tags, and push the `.gem` file to [rubygems.org](https://rubygems.org).
32
106
 
107
+
108
+ #### Running tests
109
+
110
+ To run the test suite, run
111
+ ```bash
112
+ rake test
113
+ ```
114
+
33
115
  ## Contributing
34
116
 
35
- Bug reports and pull requests are welcome on GitHub at https://github.com/[USERNAME]/solicit.
117
+ Bug reports and pull requests are welcome on GitHub at https://github.com/optimalworkshop/solicit.
118
+ Please note that this project is released with a [Contributor Code of Conduct](CONTRIBUTORS.md). By participating in this project you agree to abide by its terms.
data/Rakefile CHANGED
@@ -1,2 +1,10 @@
1
1
  require "bundler/gem_tasks"
2
- task :default => :spec
2
+ require 'rake/testtask'
3
+
4
+ desc "Run tests"
5
+ Rake::TestTask.new(:test) do |t|
6
+ t.libs << "test"
7
+ t.test_files = FileList['test/**/*_test.rb']
8
+ end
9
+
10
+ task default: :test
@@ -5,8 +5,8 @@ module Solicit
5
5
 
6
6
  def claim
7
7
  render json: ClaimPullRequest.perform(
8
- url: payload[:callback_id],
9
- label: payload.dig(:actions, 0, :value),
8
+ url: payload[:callback_id],
9
+ label: payload.dig(:actions, 0, :value),
10
10
  assignee: Solicit.contributors_map[payload.dig(:user, :name)],
11
11
  original: payload[:original_message]
12
12
  )
@@ -14,10 +14,10 @@ module Solicit
14
14
 
15
15
  def notify
16
16
  PostToSlack.perform(
17
- url: Solicit.slack_webhook_url,
18
- channel: Solicit.labels_map[payload.dig(:label, :name).to_sym],
17
+ url: Solicit.slack_webhook_url,
18
+ channel: Solicit.labels_map[payload.dig(:label, :name).to_sym],
19
19
  username: Solicit.slack_username,
20
- payload: payload
20
+ payload: payload
21
21
  )
22
22
  head :ok
23
23
  end
@@ -43,6 +43,7 @@ module Solicit
43
43
 
44
44
  def request_is_from_slack?
45
45
  return true unless Solicit.slack_secret
46
+
46
47
  signature = 'v0=' + OpenSSL::HMAC.hexdigest(
47
48
  OpenSSL::Digest::SHA256.new,
48
49
  Solicit.slack_secret,
@@ -53,6 +54,7 @@ module Solicit
53
54
 
54
55
  def request_is_from_github?
55
56
  return true unless Solicit.github_secret
57
+
56
58
  signature = 'sha1=' + OpenSSL::HMAC.hexdigest(
57
59
  OpenSSL::Digest.new('sha1'),
58
60
  Solicit.github_secret,
@@ -1,6 +1,5 @@
1
1
  module Solicit
2
2
  class ClaimPullRequest
3
-
4
3
  def initialize(url:, label:, assignee:, original:)
5
4
  @url = url
6
5
  @label = label
@@ -13,7 +12,7 @@ module Solicit
13
12
  end
14
13
 
15
14
  def perform
16
- return unless claim_pull_request.success?
15
+ return unless claim_pull_request&.success?
17
16
 
18
17
  @original[:attachments].reject! { |a| a[:callback_id] == @url } unless needs_more_assignees?
19
18
  @original[:attachments] << { color: :good, text: claim_message }
@@ -22,20 +21,31 @@ module Solicit
22
21
 
23
22
  private
24
23
 
24
+ def needs_more_assignees?
25
+ Solicit.needs_more_assignees.call(current_assignees, @label)
26
+ end
27
+
25
28
  def claim_pull_request
26
29
  return unless @assignee['handle'].presence
27
- HTTParty.post @url, default_github_params.merge(body: { assignees: Array(@assignee['handle']) }.to_json)
30
+
31
+ @claim_pull_request ||= HTTParty.post @url, default_github_params.merge(
32
+ body: { assignees: Array(@assignee['handle']) }.to_json
33
+ )
28
34
  end
29
35
 
30
- def needs_more_assignees?
31
- response = HTTParty.get @url.gsub('/assignees', ''), default_github_params
32
- Solicit.needs_more_assignees.call(response, @label)
36
+ def current_assignees
37
+ @current_assignees ||= HTTParty.get @url.gsub('/assignees', ''), default_github_params
33
38
  end
34
39
 
35
40
  def claim_message
36
- I18n.t :"engines.solicit.volunteer",
41
+ I18n.t :"engines.solicit.volunteer", claim_message_options
42
+ end
43
+
44
+ def claim_message_options
45
+ {
37
46
  name: @assignee.fetch('name', 'Someone'),
38
- emoji: @assignee.fetch('emoji', 'star2')
47
+ emoji: @assignee.fetch('emoji', 'star2'),
48
+ }
39
49
  end
40
50
 
41
51
  def default_github_params
@@ -1,41 +1,64 @@
1
1
  module Solicit
2
2
  class PostToSlack
3
+ attr_reader :text
3
4
 
4
5
  def initialize(url:, channel:, username:, payload:)
5
6
  @url = url
6
7
  @channel = channel
7
8
  @username = username
8
9
  @payload = payload
10
+ @text = I18n.t(:"engines.solicit.title", title_options)
9
11
  end
10
12
 
11
13
  def self.perform(*args)
12
- new(*args).perform
14
+ new(*args).tap(&:perform)
13
15
  end
14
16
 
15
17
  def perform
16
18
  Slack::Notifier.new(@url, channel: @channel, username: @username).ping(
17
- text: I18n.t(:"engines.solicit.title",
18
- link: @payload.dig(:pull_request, :html_url),
19
- title: @payload.dig(:pull_request, :title),
20
- label: @payload.dig(:label, :name)
21
- ),
19
+ text: text,
22
20
  attachments: [{
23
21
  title: I18n.t(:"engines.solicit.review"),
24
- callback_id: [
25
- @payload.dig(:repository, :url),
26
- 'issues',
27
- @payload.dig(:pull_request, :number),
28
- 'assignees',
29
- ].join('/'),
22
+ callback_id: callback_id,
30
23
  color: :warning,
31
- actions: [{
32
- name: :claim,
33
- text: I18n.t(:"engines.solicit.claim"),
34
- type: :button,
35
- value: @payload.dig(:label, :name),
36
- }],
24
+ actions: Array(claim_action),
37
25
  }]
38
26
  )
39
27
  end
28
+
29
+ private
30
+
31
+ def title_options
32
+ {
33
+ author: author_name,
34
+ link: @payload.dig(:pull_request, :html_url),
35
+ title: @payload.dig(:pull_request, :title),
36
+ label: @payload.dig(:label, :name),
37
+ }
38
+ end
39
+
40
+ def callback_id
41
+ [
42
+ @payload.dig(:repository, :url),
43
+ 'issues',
44
+ @payload.dig(:pull_request, :number),
45
+ 'assignees',
46
+ ].join('/')
47
+ end
48
+
49
+ def claim_action
50
+ {
51
+ name: :claim,
52
+ text: I18n.t(:"engines.solicit.claim"),
53
+ type: :button,
54
+ value: @payload.dig(:label, :name),
55
+ }
56
+ end
57
+
58
+ def author_name
59
+ Solicit.contributors_map.detect do |_, value|
60
+ value[:handle] == @payload.dig(:sender, :login)
61
+ end&.dig(1, 'name') || I18n.t(:"engines.solicit.someone")
62
+ end
40
63
  end
41
64
  end
@@ -4,4 +4,5 @@ en:
4
4
  volunteer: "%{name} has volunteered! :%{emoji}:"
5
5
  claim: "I'll do it!"
6
6
  review: "Review this PR"
7
- title: "PR <%{link}|%{title}> now has a %{label} label!"
7
+ someone: "Someone"
8
+ title: "%{author} has added a %{label} label to a PR: <%{link}|%{title}>"
@@ -1,3 +1,3 @@
1
1
  module Solicit
2
- VERSION = "0.1.0"
2
+ VERSION = '0.1.1'.freeze
3
3
  end
data/lib/solicit.rb CHANGED
@@ -13,7 +13,9 @@ module Solicit
13
13
 
14
14
  # by default, always remove the "I'll do it" button;
15
15
  # the main app can define more complicated behaviour here if necessary
16
- self.needs_more_assignees = Proc.new { false }
16
+ self.needs_more_assignees = proc { false }
17
+ self.labels_map = { go: "#general" }
18
+ self.slack_username = "Solicit"
17
19
  end
18
20
 
19
21
  class Engine < ::Rails::Engine
metadata CHANGED
@@ -1,15 +1,43 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: solicit
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.1.0
4
+ version: 0.1.1
5
5
  platform: ruby
6
6
  authors:
7
7
  - Optimal Workshop
8
8
  autorequire:
9
9
  bindir: bin
10
10
  cert_chain: []
11
- date: 2019-04-01 00:00:00.000000000 Z
11
+ date: 2019-05-13 00:00:00.000000000 Z
12
12
  dependencies:
13
+ - !ruby/object:Gem::Dependency
14
+ name: httparty
15
+ requirement: !ruby/object:Gem::Requirement
16
+ requirements:
17
+ - - "~>"
18
+ - !ruby/object:Gem::Version
19
+ version: '0.16'
20
+ type: :runtime
21
+ prerelease: false
22
+ version_requirements: !ruby/object:Gem::Requirement
23
+ requirements:
24
+ - - "~>"
25
+ - !ruby/object:Gem::Version
26
+ version: '0.16'
27
+ - !ruby/object:Gem::Dependency
28
+ name: json
29
+ requirement: !ruby/object:Gem::Requirement
30
+ requirements:
31
+ - - "~>"
32
+ - !ruby/object:Gem::Version
33
+ version: '2.2'
34
+ type: :runtime
35
+ prerelease: false
36
+ version_requirements: !ruby/object:Gem::Requirement
37
+ requirements:
38
+ - - "~>"
39
+ - !ruby/object:Gem::Version
40
+ version: '2.2'
13
41
  - !ruby/object:Gem::Dependency
14
42
  name: rails
15
43
  requirement: !ruby/object:Gem::Requirement
@@ -31,61 +59,75 @@ dependencies:
31
59
  - !ruby/object:Gem::Version
32
60
  version: 5.1.6.1
33
61
  - !ruby/object:Gem::Dependency
34
- name: httparty
62
+ name: slack-notifier
35
63
  requirement: !ruby/object:Gem::Requirement
36
64
  requirements:
37
65
  - - "~>"
38
66
  - !ruby/object:Gem::Version
39
- version: '0.16'
67
+ version: '2.3'
40
68
  type: :runtime
41
69
  prerelease: false
42
70
  version_requirements: !ruby/object:Gem::Requirement
43
71
  requirements:
44
72
  - - "~>"
45
73
  - !ruby/object:Gem::Version
46
- version: '0.16'
74
+ version: '2.3'
47
75
  - !ruby/object:Gem::Dependency
48
- name: json
76
+ name: mocha
49
77
  requirement: !ruby/object:Gem::Requirement
50
78
  requirements:
51
79
  - - "~>"
52
80
  - !ruby/object:Gem::Version
53
- version: '2.2'
54
- type: :runtime
81
+ version: '1.8'
82
+ type: :development
55
83
  prerelease: false
56
84
  version_requirements: !ruby/object:Gem::Requirement
57
85
  requirements:
58
86
  - - "~>"
59
87
  - !ruby/object:Gem::Version
60
- version: '2.2'
88
+ version: '1.8'
61
89
  - !ruby/object:Gem::Dependency
62
- name: slack-notifier
90
+ name: pry
63
91
  requirement: !ruby/object:Gem::Requirement
64
92
  requirements:
65
93
  - - "~>"
66
94
  - !ruby/object:Gem::Version
67
- version: '2.3'
68
- type: :runtime
95
+ version: '0.12'
96
+ type: :development
69
97
  prerelease: false
70
98
  version_requirements: !ruby/object:Gem::Requirement
71
99
  requirements:
72
100
  - - "~>"
73
101
  - !ruby/object:Gem::Version
74
- version: '2.3'
102
+ version: '0.12'
103
+ - !ruby/object:Gem::Dependency
104
+ name: shoulda
105
+ requirement: !ruby/object:Gem::Requirement
106
+ requirements:
107
+ - - "~>"
108
+ - !ruby/object:Gem::Version
109
+ version: '3.1'
110
+ type: :development
111
+ prerelease: false
112
+ version_requirements: !ruby/object:Gem::Requirement
113
+ requirements:
114
+ - - "~>"
115
+ - !ruby/object:Gem::Version
116
+ version: '3.1'
75
117
  - !ruby/object:Gem::Dependency
76
118
  name: sqlite3
77
119
  requirement: !ruby/object:Gem::Requirement
78
120
  requirements:
79
- - - ">="
121
+ - - "~>"
80
122
  - !ruby/object:Gem::Version
81
- version: '0'
123
+ version: '1.4'
82
124
  type: :development
83
125
  prerelease: false
84
126
  version_requirements: !ruby/object:Gem::Requirement
85
127
  requirements:
86
- - - ">="
128
+ - - "~>"
87
129
  - !ruby/object:Gem::Version
88
- version: '0'
130
+ version: '1.4'
89
131
  description: ''
90
132
  email:
91
133
  executables: []
@@ -101,7 +143,7 @@ files:
101
143
  - config/routes.rb
102
144
  - lib/solicit.rb
103
145
  - lib/solicit/version.rb
104
- homepage:
146
+ homepage: https://github.com/optimalworkshop/solicit
105
147
  licenses:
106
148
  - MIT
107
149
  metadata: {}
@@ -120,8 +162,7 @@ required_rubygems_version: !ruby/object:Gem::Requirement
120
162
  - !ruby/object:Gem::Version
121
163
  version: '0'
122
164
  requirements: []
123
- rubyforge_project:
124
- rubygems_version: 2.7.8
165
+ rubygems_version: 3.0.3
125
166
  signing_key:
126
167
  specification_version: 4
127
168
  summary: Solicit reviewers for your github pull requests on Slack