tokite 0.4.1 → 0.5.0
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +5 -5
- data/README.md +6 -3
- data/app/controllers/tokite/repositories_controller.rb +20 -6
- data/app/controllers/tokite/rules_controller.rb +1 -1
- data/app/models/tokite/hook.rb +17 -2
- data/app/models/tokite/hook_event/issue_comment.rb +4 -3
- data/app/models/tokite/hook_event/issues.rb +3 -2
- data/app/models/tokite/hook_event/pull_request.rb +19 -2
- data/app/models/tokite/hook_event/pull_request_review.rb +7 -7
- data/app/models/tokite/rule.rb +2 -2
- data/app/models/tokite/search_query.rb +30 -5
- data/app/views/layouts/tokite/application.html.haml +2 -0
- data/app/views/tokite/repositories/new.html.haml +2 -0
- data/app/views/tokite/rules/_form.html.haml +13 -0
- data/lib/tokite/version.rb +1 -1
- data/schema/tokite_repositories.schema +1 -0
- data/schema/tokite_rules.schema +2 -1
- metadata +18 -13
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
|
-
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
2
|
+
SHA256:
|
3
|
+
metadata.gz: 73bba954a04f930ead15e40578f8ca568af8556be9c590aebc0af83b0a10acfb
|
4
|
+
data.tar.gz: 9d1621ce6e6bd627fccc1b1a5c67b3da1fa531ea7eef6378f9d599b8765c61cf
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: 4e25b16e531706114b7ea766af525058055c2b0679d30ccd9b03fcb13f05fb19bb00071d354b2bd0e995489d408fe743ed14c3e86b910759732415b13dde8581
|
7
|
+
data.tar.gz: cd61a51ed21d5a7410b7930ac77c8c1d6022c7853f081546333fa069f4a09c4339e5ec1b84d8de3aa7c8eda52d2433102097e971f88ea2e5a646b978f60c0b4e
|
data/README.md
CHANGED
@@ -1,4 +1,4 @@
|
|
1
|
-
# Tokite [![
|
1
|
+
# Tokite [![Gem Version](https://badge.fury.io/rb/tokite.svg)](https://badge.fury.io/rb/tokite)
|
2
2
|
|
3
3
|
Tokite send GitHub event (pull-request, issue and comment) to Slack.
|
4
4
|
|
@@ -35,8 +35,8 @@ $ ./bin/rails tokite:yarn:install
|
|
35
35
|
|
36
36
|
## Configuration
|
37
37
|
<table>
|
38
|
-
<tr><th>GITHUB_CLIENT_ID</th><td>
|
39
|
-
<tr><th>GITHUB_CLIENT_SECRET</th><td>
|
38
|
+
<tr><th>GITHUB_CLIENT_ID</th><td>GitHub OAuth2 client ID</td></tr>
|
39
|
+
<tr><th>GITHUB_CLIENT_SECRET</th><td>GitHub OAuth2 client secret</td></tr>
|
40
40
|
<tr><th>GITHUB_HOST (optional)</th><td>GitHub Enterprise host</td></tr>
|
41
41
|
<tr><th>SECRET_KEY_BASE</th><td><code>rails secret</code> key</td></tr>
|
42
42
|
<tr><th>SLACK_WEBHOOK_URL</th><td>Slack incoming webhook url</td></tr>
|
@@ -73,6 +73,9 @@ Tokite support only below events now.
|
|
73
73
|
<tr><td>event:</td><td>Match event type pull_request, issues, issue_comment, pull_request_review, pull_request_review_comment.</td><td>event:/pull_request|issues|pull_request_review|pull_request_review_comment/</td></tr>
|
74
74
|
<tr><td>body:</td><td>Match body text.</td><td>body:"review please"</td></tr>
|
75
75
|
<tr><td>user:</td><td>Match user name.</td><td>user:hogelog</td></tr>
|
76
|
+
<tr><td>label:</td><td>Match pull_request or issue label.</td><td>label:Feature</td></tr>
|
76
77
|
<tr><td>review_state:</td><td>Match pull_request_review state.</td><td>review_state:/commented|approved|changes_requested/</td></tr>
|
78
|
+
<tr><td>requested_reviewer:</td><td>Match user name of review requested reviewer</td><td>requested_reviewer:hogelog</td></tr>
|
79
|
+
<tr><td>requested_team:</td><td>Match team name of review requested team</td><td>requested_team:cookpad/chef</td></tr>
|
77
80
|
<tr><td>unspecified</td><td>Match title or body field.</td><td>review please</td></tr>
|
78
81
|
</table>
|
@@ -7,9 +7,12 @@ module Tokite
|
|
7
7
|
end
|
8
8
|
|
9
9
|
def new
|
10
|
-
github_repos = octokit_client.repositories.
|
10
|
+
github_repos = octokit_client.repositories.
|
11
|
+
select{|r| r.permissions.admin }.
|
12
|
+
delete_if(&:fork).
|
13
|
+
delete_if(&:archived)
|
11
14
|
@repositories = github_repos.map do |repo|
|
12
|
-
Repository.new(name: repo.full_name, url: repo.html_url)
|
15
|
+
Repository.new(name: repo.full_name, url: repo.html_url, private: repo.private)
|
13
16
|
end
|
14
17
|
Repository.all.pluck(:name).each do |existing_name|
|
15
18
|
@repositories.delete_if {|repo| repo.name == existing_name }
|
@@ -17,11 +20,22 @@ module Tokite
|
|
17
20
|
end
|
18
21
|
|
19
22
|
def create
|
20
|
-
params[:names].
|
21
|
-
|
22
|
-
|
23
|
+
if params[:names].nil?
|
24
|
+
flash[:error] = "Error: No repository was selected"
|
25
|
+
else
|
26
|
+
github_repos = params[:names].map do |name|
|
27
|
+
octokit_client.repository(name)
|
28
|
+
end
|
29
|
+
errors = github_repos.select(&:archived).map(&:full_name)
|
30
|
+
if errors != []
|
31
|
+
flash[:error] = %(Error: The following repositories have been archived: #{errors.join(", ")})
|
32
|
+
else
|
33
|
+
github_repos.each do |repo|
|
34
|
+
Repository.hook!(octokit_client, repo)
|
35
|
+
end
|
36
|
+
flash[:info] = "Import repositories."
|
37
|
+
end
|
23
38
|
end
|
24
|
-
flash[:info] = "Import repositories."
|
25
39
|
redirect_to repositories_path
|
26
40
|
end
|
27
41
|
|
@@ -46,7 +46,7 @@ module Tokite
|
|
46
46
|
private
|
47
47
|
|
48
48
|
def rule_params
|
49
|
-
params.require(:rule).permit(:user_id, :name, :query, :channel, :icon_emoji, :additional_text)
|
49
|
+
params.require(:rule).permit(:user_id, :name, :query, :channel, :icon_emoji, :additional_text, :display_name)
|
50
50
|
end
|
51
51
|
end
|
52
52
|
end
|
data/app/models/tokite/hook.rb
CHANGED
@@ -32,6 +32,7 @@ module Tokite
|
|
32
32
|
if payloads.none? {|payload| payload[:channel] == rule.channel && payload[:emoji] == emoji && payload[:additional_text] == additional_text }
|
33
33
|
payloads << {
|
34
34
|
channel: rule.channel,
|
35
|
+
username: rule.display_name.presence || "tokite",
|
35
36
|
text: event.slack_text,
|
36
37
|
emoji: emoji,
|
37
38
|
additional_text: additional_text,
|
@@ -40,8 +41,22 @@ module Tokite
|
|
40
41
|
end
|
41
42
|
end
|
42
43
|
payloads.each do |payload|
|
43
|
-
notify!(
|
44
|
-
|
44
|
+
notify!(
|
45
|
+
channel: payload[:channel],
|
46
|
+
text: payload[:text],
|
47
|
+
icon_emoji: payload[:emoji],
|
48
|
+
attachments: payload[:attachments],
|
49
|
+
username: payload[:username]
|
50
|
+
)
|
51
|
+
if payload[:additional_text].present?
|
52
|
+
notify!(
|
53
|
+
channel: payload[:channel],
|
54
|
+
text: payload[:additional_text],
|
55
|
+
icon_emoji: payload[:emoji],
|
56
|
+
parse: "full",
|
57
|
+
username: payload[:username]
|
58
|
+
)
|
59
|
+
end
|
45
60
|
end
|
46
61
|
end
|
47
62
|
|
@@ -7,17 +7,18 @@ module Tokite
|
|
7
7
|
repo: hook_params[:repository][:full_name],
|
8
8
|
body: hook_params[:comment][:body],
|
9
9
|
user: hook_params[:comment][:user][:login],
|
10
|
+
label: hook_params[:issue][:labels].map { |label| label[:name] },
|
10
11
|
}
|
11
12
|
end
|
12
|
-
|
13
|
+
|
13
14
|
def notify?
|
14
15
|
%w(created).include?(hook_params[:action])
|
15
16
|
end
|
16
|
-
|
17
|
+
|
17
18
|
def slack_text
|
18
19
|
"[#{hook_params[:repository][:full_name]}] New comment by #{hook_params[:comment][:user][:login]} on issue <#{hook_params[:comment][:html_url]}|##{hook_params[:issue][:number]}: #{hook_params[:issue][:title]}>"
|
19
20
|
end
|
20
|
-
|
21
|
+
|
21
22
|
def slack_attachment
|
22
23
|
{
|
23
24
|
fallback: hook_params[:comment][:body],
|
@@ -6,8 +6,9 @@ module Tokite
|
|
6
6
|
event: "issues",
|
7
7
|
repo: hook_params[:repository][:full_name],
|
8
8
|
title: hook_params[:issue][:title],
|
9
|
-
body: hook_params[:issue][:body],
|
9
|
+
body: hook_params[:issue][:body] || "",
|
10
10
|
user: hook_params[:issue][:user][:login],
|
11
|
+
label: hook_params[:issue][:labels].map { |label| label[:name] },
|
11
12
|
}
|
12
13
|
end
|
13
14
|
|
@@ -24,7 +25,7 @@ module Tokite
|
|
24
25
|
title: "##{hook_params[:issue][:number]} #{hook_params[:issue][:title]}",
|
25
26
|
title_link: hook_params[:issue][:html_url],
|
26
27
|
fallback: "#{hook_params[:issue][:title]}\n#{hook_params[:issue][:body]}",
|
27
|
-
text: hook_params[:issue][:body],
|
28
|
+
text: hook_params[:issue][:body] || "",
|
28
29
|
color: "good",
|
29
30
|
}
|
30
31
|
end
|
@@ -8,15 +8,32 @@ module Tokite
|
|
8
8
|
title: hook_params[:pull_request][:title],
|
9
9
|
body: hook_params[:pull_request][:body],
|
10
10
|
user: hook_params[:pull_request][:user][:login],
|
11
|
+
label: hook_params[:pull_request][:labels].map { |label| label[:name] },
|
12
|
+
requested_reviewer: hook_params[:requested_reviewer]&.[](:login) || hook_params[:pull_request][:requested_reviewers].map { |reviewer| reviewer[:login] },
|
13
|
+
requested_team: hook_params[:pull_request][:requested_teams].map { |team| parse_team_name(team) },
|
11
14
|
}
|
12
15
|
end
|
13
16
|
|
17
|
+
def parse_team_name(team)
|
18
|
+
html_url = team[:html_url]
|
19
|
+
if /\/orgs\/(?<org_name>[^\s\/]+)\/teams\/(?<team_name>[^\s\/]+)\z/ =~ html_url
|
20
|
+
org_name + "/" + team_name
|
21
|
+
else
|
22
|
+
team[:slug] || team[:name]
|
23
|
+
end
|
24
|
+
end
|
25
|
+
|
14
26
|
def notify?
|
15
|
-
%w(opened).include?(hook_params[:action])
|
27
|
+
%w(opened review_requested).include?(hook_params[:action])
|
16
28
|
end
|
17
29
|
|
18
30
|
def slack_text
|
19
|
-
|
31
|
+
case hook_params[:action]
|
32
|
+
when 'opened'
|
33
|
+
"[#{hook_params[:repository][:full_name]}] Pull request submitted by <#{hook_params[:pull_request][:user][:html_url]}|#{hook_params[:pull_request][:user][:login]}>"
|
34
|
+
when 'review_requested'
|
35
|
+
"[#{hook_params[:repository][:full_name]}] Pull request review requested by <#{hook_params[:pull_request][:user][:html_url]}|#{hook_params[:pull_request][:user][:login]}>"
|
36
|
+
end
|
20
37
|
end
|
21
38
|
|
22
39
|
def slack_attachment
|
@@ -5,16 +5,17 @@ module Tokite
|
|
5
5
|
{
|
6
6
|
event: "pull_request_review",
|
7
7
|
repo: hook_params[:repository][:full_name],
|
8
|
-
body: hook_params[:review][:body],
|
8
|
+
body: hook_params[:review][:body] || "",
|
9
9
|
user: hook_params[:review][:user][:login],
|
10
10
|
review_state: hook_params[:review][:state],
|
11
11
|
}
|
12
12
|
end
|
13
13
|
|
14
14
|
def notify?
|
15
|
-
|
16
|
-
|
17
|
-
|
15
|
+
if hook_params[:action] != "submitted"
|
16
|
+
false
|
17
|
+
elsif hook_params[:review][:state] == "commented"
|
18
|
+
hook_params[:review][:body] != nil
|
18
19
|
else
|
19
20
|
true
|
20
21
|
end
|
@@ -35,7 +36,6 @@ module Tokite
|
|
35
36
|
end
|
36
37
|
|
37
38
|
def slack_attachment
|
38
|
-
return unless hook_params[:review][:body]
|
39
39
|
case hook_params[:review][:state]
|
40
40
|
when "commented"
|
41
41
|
when "approved"
|
@@ -44,8 +44,8 @@ module Tokite
|
|
44
44
|
color = "warning"
|
45
45
|
end
|
46
46
|
{
|
47
|
-
fallback: hook_params[:review][:body],
|
48
|
-
text: hook_params[:review][:body],
|
47
|
+
fallback: hook_params[:review][:body] || "",
|
48
|
+
text: hook_params[:review][:body] || "",
|
49
49
|
color: color,
|
50
50
|
}
|
51
51
|
end
|
data/app/models/tokite/rule.rb
CHANGED
@@ -6,7 +6,11 @@ module Tokite
|
|
6
6
|
|
7
7
|
DEFAULT_FIELDS = %i(title body)
|
8
8
|
|
9
|
-
class
|
9
|
+
class QueryError < StandardError
|
10
|
+
end
|
11
|
+
class QueryParseError < QueryError
|
12
|
+
end
|
13
|
+
class QueryRegexpError < QueryError
|
10
14
|
end
|
11
15
|
|
12
16
|
class Parser < Parslet::Parser
|
@@ -37,7 +41,16 @@ module Tokite
|
|
37
41
|
def self.parse(query)
|
38
42
|
Array.wrap(parser.parse(query))
|
39
43
|
rescue Parslet::ParseFailed => e
|
40
|
-
raise
|
44
|
+
raise QueryParseError, e
|
45
|
+
end
|
46
|
+
|
47
|
+
def self.validate(query)
|
48
|
+
tree = SearchQuery.parse(query)
|
49
|
+
tree.each do |word|
|
50
|
+
Regexp.compile(word[:regexp_word].to_s, Regexp::IGNORECASE) if word[:regexp_word]
|
51
|
+
end
|
52
|
+
rescue RegexpError => e
|
53
|
+
raise QueryRegexpError, e
|
41
54
|
end
|
42
55
|
|
43
56
|
def initialize(query)
|
@@ -49,13 +62,25 @@ module Tokite
|
|
49
62
|
tree.all? do |word|
|
50
63
|
field = word[:field]
|
51
64
|
if field
|
52
|
-
targets =
|
65
|
+
targets =
|
66
|
+
case doc[field.to_sym]
|
67
|
+
when Array
|
68
|
+
doc[field.to_sym].map(&:downcase)
|
69
|
+
when nil
|
70
|
+
[]
|
71
|
+
else
|
72
|
+
[doc[field.to_sym].downcase]
|
73
|
+
end
|
53
74
|
else
|
54
75
|
targets = DEFAULT_FIELDS.map{|field| doc[field]&.downcase }.compact
|
55
76
|
end
|
56
77
|
if word[:regexp_word]
|
57
|
-
|
58
|
-
|
78
|
+
begin
|
79
|
+
regexp = Regexp.compile(word[:regexp_word].to_s, Regexp::IGNORECASE)
|
80
|
+
matched = targets.any?{|text| regexp.match?(text) }
|
81
|
+
rescue RegexpError
|
82
|
+
matched = false
|
83
|
+
end
|
59
84
|
else
|
60
85
|
value = word[:word].to_s.downcase
|
61
86
|
matched = targets.any?{|text| text.index(value) }
|
@@ -4,6 +4,7 @@
|
|
4
4
|
= form_text_field f, :channel, size: 40, class: "input"
|
5
5
|
= form_text_field f, :icon_emoji, size: 40, class: "input"
|
6
6
|
= form_text_field f, :additional_text, size: 40, class: "input"
|
7
|
+
= form_text_field f, :display_name, size: 40, class: "input"
|
7
8
|
.field.columns
|
8
9
|
.column.is-8= f.submit "Update", class: "button is-primary"
|
9
10
|
- if @rule.persisted?
|
@@ -58,10 +59,22 @@
|
|
58
59
|
%td user:
|
59
60
|
%td Match user name.
|
60
61
|
%td user:hogelog
|
62
|
+
%tr
|
63
|
+
%td label:
|
64
|
+
%td Match pull_request or issue label.
|
65
|
+
%td label:Feature
|
61
66
|
%tr
|
62
67
|
%td review_state:
|
63
68
|
%td Match pull_request_review state.
|
64
69
|
%td review_state:/commented|approved|changes_requested/
|
70
|
+
%tr
|
71
|
+
%td requested_reviewer:
|
72
|
+
%td Match user name of review requested reviewer.
|
73
|
+
%td requested_reviewer:hogelog
|
74
|
+
%tr
|
75
|
+
%td requested_team:
|
76
|
+
%td Match team name of review requested team.
|
77
|
+
%td requested_team:cookpad/chef
|
65
78
|
%tr
|
66
79
|
%td unspecified
|
67
80
|
%td Match title or body field.
|
data/lib/tokite/version.rb
CHANGED
@@ -2,6 +2,7 @@ create_table "tokite_repositories", force: :cascade do |t|
|
|
2
2
|
t.string "name", limit: 200, null: false
|
3
3
|
t.string "url", limit: 200, null: false
|
4
4
|
t.datetime "created_at", null: false
|
5
|
+
t.boolean "private", null: false, default: false
|
5
6
|
end
|
6
7
|
|
7
8
|
add_index "tokite_repositories", ["name"], name: "tokite_repositories_uniq_name", unique: true, using: :btree
|
data/schema/tokite_rules.schema
CHANGED
@@ -3,10 +3,11 @@ create_table "tokite_rules", force: :cascade do |t|
|
|
3
3
|
t.string "name", limit: 50, null: false
|
4
4
|
t.string "query", limit: 2000, null: false
|
5
5
|
t.string "channel", limit: 100, null: false
|
6
|
-
t.string "icon_emoji", limit:
|
6
|
+
t.string "icon_emoji", limit: 30, null: false, default: ""
|
7
7
|
t.string "additional_text", limit: 200, null: false, default: ""
|
8
8
|
t.datetime "created_at", null: false
|
9
9
|
t.datetime "updated_at", null: false
|
10
|
+
t.string "display_name", limit: 60, null: false, default: ""
|
10
11
|
end
|
11
12
|
|
12
13
|
add_index "tokite_rules", ["user_id", "name"], name: "tokite_rule_uniq_name", unique: true, using: :btree
|
metadata
CHANGED
@@ -1,14 +1,14 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: tokite
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 0.
|
4
|
+
version: 0.5.0
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- hogelog
|
8
|
-
autorequire:
|
8
|
+
autorequire:
|
9
9
|
bindir: bin
|
10
10
|
cert_chain: []
|
11
|
-
date:
|
11
|
+
date: 2023-06-14 00:00:00.000000000 Z
|
12
12
|
dependencies:
|
13
13
|
- !ruby/object:Gem::Dependency
|
14
14
|
name: rails
|
@@ -16,14 +16,20 @@ dependencies:
|
|
16
16
|
requirements:
|
17
17
|
- - "~>"
|
18
18
|
- !ruby/object:Gem::Version
|
19
|
-
version: 5.
|
19
|
+
version: '5.2'
|
20
|
+
- - ">="
|
21
|
+
- !ruby/object:Gem::Version
|
22
|
+
version: 5.2.8.1
|
20
23
|
type: :runtime
|
21
24
|
prerelease: false
|
22
25
|
version_requirements: !ruby/object:Gem::Requirement
|
23
26
|
requirements:
|
24
27
|
- - "~>"
|
25
28
|
- !ruby/object:Gem::Version
|
26
|
-
version: 5.
|
29
|
+
version: '5.2'
|
30
|
+
- - ">="
|
31
|
+
- !ruby/object:Gem::Version
|
32
|
+
version: 5.2.8.1
|
27
33
|
- !ruby/object:Gem::Dependency
|
28
34
|
name: pg
|
29
35
|
requirement: !ruby/object:Gem::Requirement
|
@@ -44,14 +50,14 @@ dependencies:
|
|
44
50
|
requirements:
|
45
51
|
- - "~>"
|
46
52
|
- !ruby/object:Gem::Version
|
47
|
-
version: '5.
|
53
|
+
version: '5.1'
|
48
54
|
type: :runtime
|
49
55
|
prerelease: false
|
50
56
|
version_requirements: !ruby/object:Gem::Requirement
|
51
57
|
requirements:
|
52
58
|
- - "~>"
|
53
59
|
- !ruby/object:Gem::Version
|
54
|
-
version: '5.
|
60
|
+
version: '5.1'
|
55
61
|
- !ruby/object:Gem::Dependency
|
56
62
|
name: haml
|
57
63
|
requirement: !ruby/object:Gem::Requirement
|
@@ -156,14 +162,14 @@ dependencies:
|
|
156
162
|
requirements:
|
157
163
|
- - ">="
|
158
164
|
- !ruby/object:Gem::Version
|
159
|
-
version:
|
165
|
+
version: 3.9.0
|
160
166
|
type: :development
|
161
167
|
prerelease: false
|
162
168
|
version_requirements: !ruby/object:Gem::Requirement
|
163
169
|
requirements:
|
164
170
|
- - ">="
|
165
171
|
- !ruby/object:Gem::Version
|
166
|
-
version:
|
172
|
+
version: 3.9.0
|
167
173
|
- !ruby/object:Gem::Dependency
|
168
174
|
name: factory_bot_rails
|
169
175
|
requirement: !ruby/object:Gem::Requirement
|
@@ -255,7 +261,7 @@ homepage: https://github.com/cookpad/tokite/
|
|
255
261
|
licenses:
|
256
262
|
- MIT
|
257
263
|
metadata: {}
|
258
|
-
post_install_message:
|
264
|
+
post_install_message:
|
259
265
|
rdoc_options: []
|
260
266
|
require_paths:
|
261
267
|
- lib
|
@@ -270,9 +276,8 @@ required_rubygems_version: !ruby/object:Gem::Requirement
|
|
270
276
|
- !ruby/object:Gem::Version
|
271
277
|
version: '0'
|
272
278
|
requirements: []
|
273
|
-
|
274
|
-
|
275
|
-
signing_key:
|
279
|
+
rubygems_version: 3.1.6
|
280
|
+
signing_key:
|
276
281
|
specification_version: 4
|
277
282
|
summary: Customizable Slack notification from GitHub
|
278
283
|
test_files: []
|