solicit 0.1.0 → 0.1.1

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.
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