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 +4 -4
- data/README.md +94 -11
- data/Rakefile +9 -1
- data/app/controllers/solicit/pull_requests_controller.rb +7 -5
- data/app/use_cases/solicit/claim_pull_request.rb +18 -8
- data/app/use_cases/solicit/post_to_slack.rb +41 -18
- data/config/locales/en.yml +2 -1
- data/lib/solicit/version.rb +1 -1
- data/lib/solicit.rb +3 -1
- metadata +61 -20
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA256:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: 239ba565f4a101f8d7e705663bba9e001c614854c40c24c4ed7ff9bf9da05896
|
4
|
+
data.tar.gz: 58a72e1639c8a440dedc7eed64d3bdf247017389505bf22992a26ddf1aefbe45
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: 9f87726122bfcf127d144098f10bf5a3e617dd03ce242eefa375bc9ca6ec19b2c40c995e90ff654db5a26f4ff52b18c60ad9bf78a547fe1f7fec367c3e4140a2
|
7
|
+
data.tar.gz: bc47411a5692d44c2c0f1f0eeb8549c548258383773664dac274488d4547532700f3594c0d57543b9175b4962ab7752463ab89d2c635add48538986daa833ddd
|
data/README.md
CHANGED
@@ -1,35 +1,118 @@
|
|
1
1
|
# Solicit
|
2
2
|
|
3
|
-
|
3
|
+
Solicit requests reviewers from your slack channel automatically!
|
4
4
|
|
5
|
-
|
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
|
13
|
+
1. Add an outgoing webhook to your github repository, with the 'pull requests' event type enabled
|
14
|
+
|
15
|
+

|
16
|
+
_NB: the 'Secret' value here will become your github_secret value_
|
10
17
|
|
18
|
+
2. [Create a slack app] for your team
|
19
|
+

|
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
|
-
|
64
|
+
https://github.blog/2013-05-16-personal-api-tokens/
|
16
65
|
|
17
|
-
|
66
|
+
##### contributors_map (required)
|
18
67
|
|
19
|
-
|
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
|
-
|
72
|
+
##### labels_map (optional)
|
22
73
|
|
23
|
-
|
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
|
-
|
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
|
-
|
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/
|
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
@@ -5,8 +5,8 @@ module Solicit
|
|
5
5
|
|
6
6
|
def claim
|
7
7
|
render json: ClaimPullRequest.perform(
|
8
|
-
url:
|
9
|
-
label:
|
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:
|
18
|
-
channel:
|
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:
|
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
|
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
|
-
|
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
|
31
|
-
|
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:
|
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
|
data/config/locales/en.yml
CHANGED
data/lib/solicit/version.rb
CHANGED
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 =
|
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.
|
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-
|
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:
|
62
|
+
name: slack-notifier
|
35
63
|
requirement: !ruby/object:Gem::Requirement
|
36
64
|
requirements:
|
37
65
|
- - "~>"
|
38
66
|
- !ruby/object:Gem::Version
|
39
|
-
version: '
|
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: '
|
74
|
+
version: '2.3'
|
47
75
|
- !ruby/object:Gem::Dependency
|
48
|
-
name:
|
76
|
+
name: mocha
|
49
77
|
requirement: !ruby/object:Gem::Requirement
|
50
78
|
requirements:
|
51
79
|
- - "~>"
|
52
80
|
- !ruby/object:Gem::Version
|
53
|
-
version: '
|
54
|
-
type: :
|
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: '
|
88
|
+
version: '1.8'
|
61
89
|
- !ruby/object:Gem::Dependency
|
62
|
-
name:
|
90
|
+
name: pry
|
63
91
|
requirement: !ruby/object:Gem::Requirement
|
64
92
|
requirements:
|
65
93
|
- - "~>"
|
66
94
|
- !ruby/object:Gem::Version
|
67
|
-
version: '
|
68
|
-
type: :
|
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: '
|
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: '
|
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: '
|
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
|
-
|
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
|