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 +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
|
+
![](/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
|
-
|
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
|