solicit 0.1.0

Sign up to get free protection for your applications and to get access to all the features.
checksums.yaml ADDED
@@ -0,0 +1,7 @@
1
+ ---
2
+ SHA256:
3
+ metadata.gz: 115b96f84a7bc69ebfabf45139d2261663e73f2b0895d0ce8456a055c124f7af
4
+ data.tar.gz: 736dca15502cdfad1c6218d083a0567938e11cf4bdc29d02c79c413b790f5625
5
+ SHA512:
6
+ metadata.gz: 0ddb5c194e2a18cb5f05b7f26817decfe1bb9f47d6d88683df6166c866e9beab6ed1919761324c41f0b63de50224bcd27c3c722d81a2346e3890484e3c208a63
7
+ data.tar.gz: 9f07848217775d9de5713802b436e3f310a3f32348f2469645a4438c6d415bc1e20539868105b70ac8ad748f063e87b8aae81570736a3452ee8aba8bcbb7bd04
data/README.md ADDED
@@ -0,0 +1,35 @@
1
+ # Solicit
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.
4
+
5
+ TODO: Delete this and the text above, and describe your gem
6
+
7
+ ## Installation
8
+
9
+ Add this line to your application's Gemfile:
10
+
11
+ ```ruby
12
+ gem 'solicit'
13
+ ```
14
+
15
+ And then execute:
16
+
17
+ $ bundle
18
+
19
+ Or install it yourself as:
20
+
21
+ $ gem install solicit
22
+
23
+ ## Usage
24
+
25
+ TODO: Write usage instructions here
26
+
27
+ ## Development
28
+
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.
30
+
31
+ 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
+
33
+ ## Contributing
34
+
35
+ Bug reports and pull requests are welcome on GitHub at https://github.com/[USERNAME]/solicit.
data/Rakefile ADDED
@@ -0,0 +1,2 @@
1
+ require "bundler/gem_tasks"
2
+ task :default => :spec
@@ -0,0 +1,68 @@
1
+ module Solicit
2
+ class PullRequestsController < ActionController::Base
3
+ before_action :ensure_can_claim, only: :claim
4
+ before_action :ensure_can_notify, only: :notify
5
+
6
+ def claim
7
+ render json: ClaimPullRequest.perform(
8
+ url: payload[:callback_id],
9
+ label: payload.dig(:actions, 0, :value),
10
+ assignee: Solicit.contributors_map[payload.dig(:user, :name)],
11
+ original: payload[:original_message]
12
+ )
13
+ end
14
+
15
+ def notify
16
+ PostToSlack.perform(
17
+ url: Solicit.slack_webhook_url,
18
+ channel: Solicit.labels_map[payload.dig(:label, :name).to_sym],
19
+ username: Solicit.slack_username,
20
+ payload: payload
21
+ )
22
+ head :ok
23
+ end
24
+
25
+ private
26
+
27
+ def ensure_can_claim
28
+ head :ok unless request_is_from_slack? &&
29
+ payload.dig(:user, :name) &&
30
+ Solicit.contributors_map.key?(payload.dig(:user, :name)) &&
31
+ payload[:callback_id] &&
32
+ payload[:original_message] &&
33
+ payload.dig(:actions, 0, :value)
34
+ end
35
+
36
+ def ensure_can_notify
37
+ head :ok unless request_is_from_github? &&
38
+ payload[:action] == 'labeled' &&
39
+ Solicit.labels_map.key?(payload.dig(:label, :name).to_sym) &&
40
+ payload.dig(:pull_request, :number) &&
41
+ payload.dig(:repository, :url)
42
+ end
43
+
44
+ def request_is_from_slack?
45
+ return true unless Solicit.slack_secret
46
+ signature = 'v0=' + OpenSSL::HMAC.hexdigest(
47
+ OpenSSL::Digest::SHA256.new,
48
+ Solicit.slack_secret,
49
+ ['v0', request.headers['X-Slack-Request-Timestamp'], request.body.read].join(':')
50
+ )
51
+ Rack::Utils.secure_compare(signature, request.headers['X-Slack-Signature'])
52
+ end
53
+
54
+ def request_is_from_github?
55
+ return true unless Solicit.github_secret
56
+ signature = 'sha1=' + OpenSSL::HMAC.hexdigest(
57
+ OpenSSL::Digest.new('sha1'),
58
+ Solicit.github_secret,
59
+ request.body.read
60
+ )
61
+ Rack::Utils.secure_compare(signature, request.headers['HTTP_X_HUB_SIGNATURE'])
62
+ end
63
+
64
+ def payload
65
+ @payload ||= JSON.parse(params.require(:payload)).with_indifferent_access
66
+ end
67
+ end
68
+ end
@@ -0,0 +1,45 @@
1
+ module Solicit
2
+ class ClaimPullRequest
3
+
4
+ def initialize(url:, label:, assignee:, original:)
5
+ @url = url
6
+ @label = label
7
+ @assignee = assignee
8
+ @original = original
9
+ end
10
+
11
+ def self.perform(*args)
12
+ new(*args).perform
13
+ end
14
+
15
+ def perform
16
+ return unless claim_pull_request.success?
17
+
18
+ @original[:attachments].reject! { |a| a[:callback_id] == @url } unless needs_more_assignees?
19
+ @original[:attachments] << { color: :good, text: claim_message }
20
+ @original
21
+ end
22
+
23
+ private
24
+
25
+ def claim_pull_request
26
+ return unless @assignee['handle'].presence
27
+ HTTParty.post @url, default_github_params.merge(body: { assignees: Array(@assignee['handle']) }.to_json)
28
+ end
29
+
30
+ def needs_more_assignees?
31
+ response = HTTParty.get @url.gsub('/assignees', ''), default_github_params
32
+ Solicit.needs_more_assignees.call(response, @label)
33
+ end
34
+
35
+ def claim_message
36
+ I18n.t :"engines.solicit.volunteer",
37
+ name: @assignee.fetch('name', 'Someone'),
38
+ emoji: @assignee.fetch('emoji', 'star2')
39
+ end
40
+
41
+ def default_github_params
42
+ { query: { access_token: Solicit.github_api_token } }
43
+ end
44
+ end
45
+ end
@@ -0,0 +1,41 @@
1
+ module Solicit
2
+ class PostToSlack
3
+
4
+ def initialize(url:, channel:, username:, payload:)
5
+ @url = url
6
+ @channel = channel
7
+ @username = username
8
+ @payload = payload
9
+ end
10
+
11
+ def self.perform(*args)
12
+ new(*args).perform
13
+ end
14
+
15
+ def perform
16
+ 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
+ ),
22
+ attachments: [{
23
+ 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('/'),
30
+ color: :warning,
31
+ actions: [{
32
+ name: :claim,
33
+ text: I18n.t(:"engines.solicit.claim"),
34
+ type: :button,
35
+ value: @payload.dig(:label, :name),
36
+ }],
37
+ }]
38
+ )
39
+ end
40
+ end
41
+ end
@@ -0,0 +1,7 @@
1
+ en:
2
+ engines:
3
+ solicit:
4
+ volunteer: "%{name} has volunteered! :%{emoji}:"
5
+ claim: "I'll do it!"
6
+ review: "Review this PR"
7
+ title: "PR <%{link}|%{title}> now has a %{label} label!"
data/config/routes.rb ADDED
@@ -0,0 +1,6 @@
1
+ Solicit::Engine.routes.draw do
2
+ resources :pull_requests, only: [] do
3
+ post :claim, on: :collection
4
+ post :notify, on: :collection
5
+ end
6
+ end
@@ -0,0 +1,3 @@
1
+ module Solicit
2
+ VERSION = "0.1.0"
3
+ end
data/lib/solicit.rb ADDED
@@ -0,0 +1,26 @@
1
+ require "solicit/version"
2
+
3
+ module Solicit
4
+ class << self
5
+ mattr_accessor :labels_map,
6
+ :contributors_map,
7
+ :slack_webhook_url,
8
+ :slack_username,
9
+ :slack_secret,
10
+ :github_api_token,
11
+ :github_secret,
12
+ :needs_more_assignees
13
+
14
+ # by default, always remove the "I'll do it" button;
15
+ # the main app can define more complicated behaviour here if necessary
16
+ self.needs_more_assignees = Proc.new { false }
17
+ end
18
+
19
+ class Engine < ::Rails::Engine
20
+ isolate_namespace Solicit
21
+
22
+ config.before_initialize do
23
+ config.i18n.load_path += Dir["#{config.root}/config/locales/**/*.yml"]
24
+ end
25
+ end
26
+ end
metadata ADDED
@@ -0,0 +1,128 @@
1
+ --- !ruby/object:Gem::Specification
2
+ name: solicit
3
+ version: !ruby/object:Gem::Version
4
+ version: 0.1.0
5
+ platform: ruby
6
+ authors:
7
+ - Optimal Workshop
8
+ autorequire:
9
+ bindir: bin
10
+ cert_chain: []
11
+ date: 2019-04-01 00:00:00.000000000 Z
12
+ dependencies:
13
+ - !ruby/object:Gem::Dependency
14
+ name: rails
15
+ requirement: !ruby/object:Gem::Requirement
16
+ requirements:
17
+ - - "~>"
18
+ - !ruby/object:Gem::Version
19
+ version: 5.1.6
20
+ - - ">="
21
+ - !ruby/object:Gem::Version
22
+ version: 5.1.6.1
23
+ type: :runtime
24
+ prerelease: false
25
+ version_requirements: !ruby/object:Gem::Requirement
26
+ requirements:
27
+ - - "~>"
28
+ - !ruby/object:Gem::Version
29
+ version: 5.1.6
30
+ - - ">="
31
+ - !ruby/object:Gem::Version
32
+ version: 5.1.6.1
33
+ - !ruby/object:Gem::Dependency
34
+ name: httparty
35
+ requirement: !ruby/object:Gem::Requirement
36
+ requirements:
37
+ - - "~>"
38
+ - !ruby/object:Gem::Version
39
+ version: '0.16'
40
+ type: :runtime
41
+ prerelease: false
42
+ version_requirements: !ruby/object:Gem::Requirement
43
+ requirements:
44
+ - - "~>"
45
+ - !ruby/object:Gem::Version
46
+ version: '0.16'
47
+ - !ruby/object:Gem::Dependency
48
+ name: json
49
+ requirement: !ruby/object:Gem::Requirement
50
+ requirements:
51
+ - - "~>"
52
+ - !ruby/object:Gem::Version
53
+ version: '2.2'
54
+ type: :runtime
55
+ prerelease: false
56
+ version_requirements: !ruby/object:Gem::Requirement
57
+ requirements:
58
+ - - "~>"
59
+ - !ruby/object:Gem::Version
60
+ version: '2.2'
61
+ - !ruby/object:Gem::Dependency
62
+ name: slack-notifier
63
+ requirement: !ruby/object:Gem::Requirement
64
+ requirements:
65
+ - - "~>"
66
+ - !ruby/object:Gem::Version
67
+ version: '2.3'
68
+ type: :runtime
69
+ prerelease: false
70
+ version_requirements: !ruby/object:Gem::Requirement
71
+ requirements:
72
+ - - "~>"
73
+ - !ruby/object:Gem::Version
74
+ version: '2.3'
75
+ - !ruby/object:Gem::Dependency
76
+ name: sqlite3
77
+ requirement: !ruby/object:Gem::Requirement
78
+ requirements:
79
+ - - ">="
80
+ - !ruby/object:Gem::Version
81
+ version: '0'
82
+ type: :development
83
+ prerelease: false
84
+ version_requirements: !ruby/object:Gem::Requirement
85
+ requirements:
86
+ - - ">="
87
+ - !ruby/object:Gem::Version
88
+ version: '0'
89
+ description: ''
90
+ email:
91
+ executables: []
92
+ extensions: []
93
+ extra_rdoc_files: []
94
+ files:
95
+ - README.md
96
+ - Rakefile
97
+ - app/controllers/solicit/pull_requests_controller.rb
98
+ - app/use_cases/solicit/claim_pull_request.rb
99
+ - app/use_cases/solicit/post_to_slack.rb
100
+ - config/locales/en.yml
101
+ - config/routes.rb
102
+ - lib/solicit.rb
103
+ - lib/solicit/version.rb
104
+ homepage:
105
+ licenses:
106
+ - MIT
107
+ metadata: {}
108
+ post_install_message:
109
+ rdoc_options: []
110
+ require_paths:
111
+ - lib
112
+ required_ruby_version: !ruby/object:Gem::Requirement
113
+ requirements:
114
+ - - ">="
115
+ - !ruby/object:Gem::Version
116
+ version: '0'
117
+ required_rubygems_version: !ruby/object:Gem::Requirement
118
+ requirements:
119
+ - - ">="
120
+ - !ruby/object:Gem::Version
121
+ version: '0'
122
+ requirements: []
123
+ rubyforge_project:
124
+ rubygems_version: 2.7.8
125
+ signing_key:
126
+ specification_version: 4
127
+ summary: Solicit reviewers for your github pull requests on Slack
128
+ test_files: []