smart_todo 1.0.2 → 1.3.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/.github/workflows/build.yml +27 -0
- data/.github/workflows/rubocop.yml +22 -0
- data/.rubocop.yml +3 -3
- data/Gemfile +2 -2
- data/Gemfile.lock +33 -26
- data/README.md +1 -1
- data/dev.yml +5 -1
- data/exe/smart_todo +3 -5
- data/lib/smart_todo/cli.rb +17 -11
- data/lib/smart_todo/dispatchers/base.rb +94 -0
- data/lib/smart_todo/dispatchers/output.rb +15 -0
- data/lib/smart_todo/dispatchers/slack.rb +67 -0
- data/lib/smart_todo/events/date.rb +1 -1
- data/lib/smart_todo/events/gem_bump.rb +57 -0
- data/lib/smart_todo/events/gem_release.rb +5 -5
- data/lib/smart_todo/events/issue_close.rb +7 -7
- data/lib/smart_todo/events.rb +11 -2
- data/lib/smart_todo/parser/comment_parser.rb +1 -1
- data/lib/smart_todo/parser/metadata_parser.rb +10 -7
- data/lib/smart_todo/parser/todo_node.rb +3 -3
- data/lib/smart_todo/slack_client.rb +20 -19
- data/lib/smart_todo/version.rb +1 -1
- data/lib/smart_todo.rb +15 -9
- data/lib/smart_todo_cop.rb +6 -4
- data/service.yml +1 -0
- data/smart_todo.gemspec +7 -6
- metadata +27 -23
- data/.rubocop-http---shopify-github-io-ruby-style-guide-rubocop-yml +0 -1027
- data/.travis.yml +0 -15
- data/CHANGELOG.md +0 -24
- data/lib/smart_todo/dispatcher.rb +0 -99
checksums.yaml
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
---
|
|
2
2
|
SHA256:
|
|
3
|
-
metadata.gz:
|
|
4
|
-
data.tar.gz:
|
|
3
|
+
metadata.gz: dc3a4665a0110180692321fbdd788be8df4155de16bf75b2967f4c330a759e72
|
|
4
|
+
data.tar.gz: 414a198aa06035126890b002f6552779c73d9f6f84eeeaa904ba2132daa7d4d9
|
|
5
5
|
SHA512:
|
|
6
|
-
metadata.gz:
|
|
7
|
-
data.tar.gz:
|
|
6
|
+
metadata.gz: 8e0c57b0849a90cd1be9699d264167e7286d622eb42e02cc7dd13231ab41d76d815a455a65571f3890dda7c79dc0228692b0c72c37c50337826df137d0c0f0df
|
|
7
|
+
data.tar.gz: a62b94332abe655cb418d0238eaa24e86db0e48b109877b8192265a244795624a2188cffeb0be429f0204683394b992ce73a0932efc322b4c3ba589cadcd0af4
|
|
@@ -0,0 +1,27 @@
|
|
|
1
|
+
name: CI
|
|
2
|
+
|
|
3
|
+
on:
|
|
4
|
+
push:
|
|
5
|
+
branches: [ main ]
|
|
6
|
+
pull_request:
|
|
7
|
+
branches: [ main ]
|
|
8
|
+
|
|
9
|
+
jobs:
|
|
10
|
+
build:
|
|
11
|
+
runs-on: ubuntu-latest
|
|
12
|
+
name: Ruby ${{ matrix.version }}
|
|
13
|
+
strategy:
|
|
14
|
+
matrix:
|
|
15
|
+
version: [2.5, 2.6, 2.7, 3.0]
|
|
16
|
+
|
|
17
|
+
steps:
|
|
18
|
+
- uses: actions/checkout@v2
|
|
19
|
+
- name: Set up Ruby ${{ matrix.version }}
|
|
20
|
+
uses: ruby/setup-ruby@v1
|
|
21
|
+
with:
|
|
22
|
+
ruby-version: ${{ matrix.version }}
|
|
23
|
+
bundler-cache: true
|
|
24
|
+
- name: Run Tests
|
|
25
|
+
run: |
|
|
26
|
+
bundle exec rake test
|
|
27
|
+
|
|
@@ -0,0 +1,22 @@
|
|
|
1
|
+
name: RuboCop
|
|
2
|
+
|
|
3
|
+
on: [push, pull_request]
|
|
4
|
+
|
|
5
|
+
jobs:
|
|
6
|
+
build:
|
|
7
|
+
runs-on: ubuntu-latest
|
|
8
|
+
|
|
9
|
+
steps:
|
|
10
|
+
- uses: actions/checkout@v2
|
|
11
|
+
- name: Set up Ruby 3.0
|
|
12
|
+
uses: ruby/setup-ruby@v1
|
|
13
|
+
with:
|
|
14
|
+
ruby-version: 3.0
|
|
15
|
+
bundler-cache: true
|
|
16
|
+
- name: Install gems
|
|
17
|
+
run: |
|
|
18
|
+
bundle config path vendor/bundle
|
|
19
|
+
bundle config set without 'default development test'
|
|
20
|
+
bundle install --jobs 4 --retry 3
|
|
21
|
+
- name: Run RuboCop
|
|
22
|
+
run: bundle exec rubocop --parallel
|
data/.rubocop.yml
CHANGED
data/Gemfile
CHANGED
data/Gemfile.lock
CHANGED
|
@@ -1,36 +1,43 @@
|
|
|
1
1
|
PATH
|
|
2
2
|
remote: .
|
|
3
3
|
specs:
|
|
4
|
-
smart_todo (1.
|
|
4
|
+
smart_todo (1.3.1)
|
|
5
|
+
rexml
|
|
5
6
|
|
|
6
7
|
GEM
|
|
7
8
|
remote: https://rubygems.org/
|
|
8
9
|
specs:
|
|
9
|
-
addressable (2.
|
|
10
|
-
public_suffix (>= 2.0.2, <
|
|
11
|
-
ast (2.4.
|
|
12
|
-
crack (0.4.
|
|
13
|
-
|
|
14
|
-
hashdiff (0.
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
public_suffix (3.1.1)
|
|
10
|
+
addressable (2.8.0)
|
|
11
|
+
public_suffix (>= 2.0.2, < 5.0)
|
|
12
|
+
ast (2.4.2)
|
|
13
|
+
crack (0.4.5)
|
|
14
|
+
rexml
|
|
15
|
+
hashdiff (1.0.1)
|
|
16
|
+
minitest (5.14.4)
|
|
17
|
+
parallel (1.21.0)
|
|
18
|
+
parser (3.0.2.0)
|
|
19
|
+
ast (~> 2.4.1)
|
|
20
|
+
public_suffix (4.0.6)
|
|
21
21
|
rainbow (3.0.0)
|
|
22
|
-
rake (
|
|
23
|
-
|
|
24
|
-
|
|
22
|
+
rake (13.0.6)
|
|
23
|
+
regexp_parser (2.1.1)
|
|
24
|
+
rexml (3.2.5)
|
|
25
|
+
rubocop (1.23.0)
|
|
25
26
|
parallel (~> 1.10)
|
|
26
|
-
parser (>=
|
|
27
|
+
parser (>= 3.0.0.0)
|
|
27
28
|
rainbow (>= 2.2.2, < 4.0)
|
|
29
|
+
regexp_parser (>= 1.8, < 3.0)
|
|
30
|
+
rexml
|
|
31
|
+
rubocop-ast (>= 1.12.0, < 2.0)
|
|
28
32
|
ruby-progressbar (~> 1.7)
|
|
29
|
-
unicode-display_width (>= 1.4.0, <
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
33
|
+
unicode-display_width (>= 1.4.0, < 3.0)
|
|
34
|
+
rubocop-ast (1.13.0)
|
|
35
|
+
parser (>= 3.0.1.1)
|
|
36
|
+
rubocop-shopify (2.3.0)
|
|
37
|
+
rubocop (~> 1.22)
|
|
38
|
+
ruby-progressbar (1.11.0)
|
|
39
|
+
unicode-display_width (2.1.0)
|
|
40
|
+
webmock (3.11.2)
|
|
34
41
|
addressable (>= 2.3.6)
|
|
35
42
|
crack (>= 0.3.2)
|
|
36
43
|
hashdiff (>= 0.4.0, < 2.0.0)
|
|
@@ -39,12 +46,12 @@ PLATFORMS
|
|
|
39
46
|
ruby
|
|
40
47
|
|
|
41
48
|
DEPENDENCIES
|
|
42
|
-
bundler (
|
|
49
|
+
bundler (>= 1.17)
|
|
43
50
|
minitest (~> 5.0)
|
|
44
|
-
rake (
|
|
45
|
-
rubocop
|
|
51
|
+
rake (>= 10.0)
|
|
52
|
+
rubocop-shopify
|
|
46
53
|
smart_todo!
|
|
47
54
|
webmock
|
|
48
55
|
|
|
49
56
|
BUNDLED WITH
|
|
50
|
-
|
|
57
|
+
2.2.25
|
data/README.md
CHANGED
|
@@ -2,7 +2,7 @@
|
|
|
2
2
|
<img src="https://user-images.githubusercontent.com/8122246/61341925-b936d180-a848-11e9-95c1-0d2f398c51b1.png?raw=true" width="200">
|
|
3
3
|
</h3>
|
|
4
4
|
|
|
5
|
-
[](https://github.com/Shopify/smart_todo/actions?query=workflow%3ACI)
|
|
6
6
|
|
|
7
7
|
_SmartTodo_ is a library designed to assign users on TODO comments written in your codebase and help assignees be reminded when it's time to commit to their TODO.
|
|
8
8
|
|
data/dev.yml
CHANGED
data/exe/smart_todo
CHANGED
|
@@ -3,12 +3,10 @@
|
|
|
3
3
|
|
|
4
4
|
$LOAD_PATH.unshift("#{__dir__}/../lib")
|
|
5
5
|
|
|
6
|
-
require
|
|
6
|
+
require "smart_todo"
|
|
7
7
|
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
exit(0)
|
|
8
|
+
if ENV["ENABLE_SMART_TODO"] && !ARGV.include?("--dispatcher")
|
|
9
|
+
ARGV << "--dispatcher" << "slack"
|
|
12
10
|
end
|
|
13
11
|
|
|
14
12
|
SmartTodo::CLI.new.run
|
data/lib/smart_todo/cli.rb
CHANGED
|
@@ -15,40 +15,46 @@ module SmartTodo
|
|
|
15
15
|
def run(args = ARGV)
|
|
16
16
|
paths = define_options.parse!(args)
|
|
17
17
|
validate_options!
|
|
18
|
-
paths <<
|
|
18
|
+
paths << "." if paths.empty?
|
|
19
19
|
|
|
20
20
|
paths.each do |path|
|
|
21
21
|
normalize_path(path).each do |file|
|
|
22
22
|
parse_file(file)
|
|
23
23
|
|
|
24
|
-
STDOUT.print(
|
|
24
|
+
STDOUT.print(".")
|
|
25
25
|
STDOUT.flush
|
|
26
26
|
end
|
|
27
27
|
end
|
|
28
28
|
end
|
|
29
29
|
|
|
30
|
-
# @raise [ArgumentError]
|
|
31
|
-
#
|
|
30
|
+
# @raise [ArgumentError] In case an option needed by a dispatcher wasn't provided.
|
|
31
|
+
#
|
|
32
32
|
# @return [void]
|
|
33
33
|
def validate_options!
|
|
34
|
-
@options
|
|
35
|
-
|
|
36
|
-
@options.fetch(:fallback_channel) { raise(ArgumentError, 'Missing :fallback_channel') }
|
|
34
|
+
dispatcher.validate_options!(@options)
|
|
37
35
|
end
|
|
38
36
|
|
|
39
37
|
# @return [OptionParser] an instance of OptionParser
|
|
40
38
|
def define_options
|
|
41
39
|
OptionParser.new do |opts|
|
|
42
40
|
opts.banner = "Usage: smart_todo [options] file_or_path1 file_or_path2 ..."
|
|
43
|
-
opts.on(
|
|
41
|
+
opts.on("--slack_token TOKEN") do |token|
|
|
44
42
|
@options[:slack_token] = token
|
|
45
43
|
end
|
|
46
|
-
opts.on(
|
|
44
|
+
opts.on("--fallback_channel CHANNEL") do |channel|
|
|
47
45
|
@options[:fallback_channel] = channel
|
|
48
46
|
end
|
|
47
|
+
opts.on("--dispatcher DISPATCHER") do |dispatcher|
|
|
48
|
+
@options[:dispatcher] = dispatcher
|
|
49
|
+
end
|
|
49
50
|
end
|
|
50
51
|
end
|
|
51
52
|
|
|
53
|
+
# @return [Class] a Dispatchers::Base subclass
|
|
54
|
+
def dispatcher
|
|
55
|
+
@dispatcher ||= Dispatchers::Base.class_for(@options[:dispatcher])
|
|
56
|
+
end
|
|
57
|
+
|
|
52
58
|
# @param path [String] a path to a file or directory
|
|
53
59
|
# @return [Array<String>] all the directories the parser should run on
|
|
54
60
|
def normalize_path(path)
|
|
@@ -61,13 +67,13 @@ module SmartTodo
|
|
|
61
67
|
|
|
62
68
|
# @param file [String] a path to a file
|
|
63
69
|
def parse_file(file)
|
|
64
|
-
Parser::CommentParser.new(File.read(file, encoding:
|
|
70
|
+
Parser::CommentParser.new(File.read(file, encoding: "UTF-8")).parse.each do |todo_node|
|
|
65
71
|
event_message = nil
|
|
66
72
|
event_met = todo_node.metadata.events.find do |event|
|
|
67
73
|
event_message = Events.public_send(event.method_name, *event.arguments)
|
|
68
74
|
end
|
|
69
75
|
|
|
70
|
-
|
|
76
|
+
dispatcher.new(event_message, todo_node, file, @options).dispatch if event_met
|
|
71
77
|
end
|
|
72
78
|
end
|
|
73
79
|
end
|
|
@@ -0,0 +1,94 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
module SmartTodo
|
|
4
|
+
module Dispatchers
|
|
5
|
+
class Base
|
|
6
|
+
# Factory pattern to retrive the right dispatcher class.
|
|
7
|
+
#
|
|
8
|
+
# @param dispatcher [String]
|
|
9
|
+
#
|
|
10
|
+
# @return [Class]
|
|
11
|
+
def self.class_for(dispatcher)
|
|
12
|
+
case dispatcher
|
|
13
|
+
when "slack"
|
|
14
|
+
Slack
|
|
15
|
+
when nil, "output"
|
|
16
|
+
Output
|
|
17
|
+
end
|
|
18
|
+
end
|
|
19
|
+
|
|
20
|
+
# Subclasses should define what options from the CLI they need in order
|
|
21
|
+
# to properly deliver the message. For instance the Slack dispatcher
|
|
22
|
+
# requires an API key.
|
|
23
|
+
#
|
|
24
|
+
# @param _options [Hash]
|
|
25
|
+
#
|
|
26
|
+
# @return void
|
|
27
|
+
def self.validate_options!(_options)
|
|
28
|
+
raise(NotImplemetedError, "subclass responsability")
|
|
29
|
+
end
|
|
30
|
+
|
|
31
|
+
# @param event_message [String] the success message associated
|
|
32
|
+
# a specific event
|
|
33
|
+
# @param todo_node [SmartTodo::Parser::TodoNode]
|
|
34
|
+
# @param file [String] the file containing the TODO
|
|
35
|
+
# @param options [Hash]
|
|
36
|
+
def initialize(event_message, todo_node, file, options)
|
|
37
|
+
@event_message = event_message
|
|
38
|
+
@todo_node = todo_node
|
|
39
|
+
@options = options
|
|
40
|
+
@file = file
|
|
41
|
+
@assignees = @todo_node.metadata.assignees
|
|
42
|
+
end
|
|
43
|
+
|
|
44
|
+
# This method gets called when a TODO reminder is expired and needs to be delivered.
|
|
45
|
+
# Dispatchers should implement this method to deliver the message where they need.
|
|
46
|
+
#
|
|
47
|
+
# @return void
|
|
48
|
+
def dispatch
|
|
49
|
+
raise(NotImplemetedError, "subclass responsability")
|
|
50
|
+
end
|
|
51
|
+
|
|
52
|
+
private
|
|
53
|
+
|
|
54
|
+
# Prepare the content of the message to send to the TODO assignee
|
|
55
|
+
#
|
|
56
|
+
# @param user [Hash] contain information about a user
|
|
57
|
+
# @param assignee [String] original string handle the slack message should be sent
|
|
58
|
+
# @return [String]
|
|
59
|
+
def slack_message(user, assignee)
|
|
60
|
+
header = if user.key?("fallback")
|
|
61
|
+
unexisting_user(assignee)
|
|
62
|
+
else
|
|
63
|
+
existing_user
|
|
64
|
+
end
|
|
65
|
+
|
|
66
|
+
<<~EOM
|
|
67
|
+
#{header}
|
|
68
|
+
|
|
69
|
+
You have an assigned TODO in the `#{@file}` file.
|
|
70
|
+
#{@event_message}
|
|
71
|
+
|
|
72
|
+
Here is the associated comment on your TODO:
|
|
73
|
+
|
|
74
|
+
```
|
|
75
|
+
#{@todo_node.comment.strip}
|
|
76
|
+
```
|
|
77
|
+
EOM
|
|
78
|
+
end
|
|
79
|
+
|
|
80
|
+
# Message in case a TODO's assignee doesn't exist in the Slack organization
|
|
81
|
+
#
|
|
82
|
+
# @param user [Hash]
|
|
83
|
+
# @return [String]
|
|
84
|
+
def unexisting_user(assignee)
|
|
85
|
+
"Hello :wave:,\n\n`#{assignee}` had an assigned TODO but this user or channel doesn't exist on Slack anymore."
|
|
86
|
+
end
|
|
87
|
+
|
|
88
|
+
# Hello message for user actually existing in the organization
|
|
89
|
+
def existing_user
|
|
90
|
+
"Hello :wave:,"
|
|
91
|
+
end
|
|
92
|
+
end
|
|
93
|
+
end
|
|
94
|
+
end
|
|
@@ -0,0 +1,15 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
module SmartTodo
|
|
4
|
+
module Dispatchers
|
|
5
|
+
# A simple dispatcher that will output the reminder.
|
|
6
|
+
class Output < Base
|
|
7
|
+
def self.validate_options!(_); end
|
|
8
|
+
|
|
9
|
+
# @return void
|
|
10
|
+
def dispatch
|
|
11
|
+
puts slack_message({}, nil)
|
|
12
|
+
end
|
|
13
|
+
end
|
|
14
|
+
end
|
|
15
|
+
end
|
|
@@ -0,0 +1,67 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
module SmartTodo
|
|
4
|
+
module Dispatchers
|
|
5
|
+
# Dispatcher that sends TODO reminders on Slack. Assignees can be either individual
|
|
6
|
+
# (using the associated slack email address) or a channel.
|
|
7
|
+
class Slack < Base
|
|
8
|
+
def self.validate_options!(options)
|
|
9
|
+
options[:slack_token] ||= ENV.fetch("SMART_TODO_SLACK_TOKEN") { raise(ArgumentError, "Missing :slack_token") }
|
|
10
|
+
|
|
11
|
+
options.fetch(:fallback_channel) { raise(ArgumentError, "Missing :fallback_channel") }
|
|
12
|
+
end
|
|
13
|
+
|
|
14
|
+
# Make a Slack API call to dispatch the message to each assignee
|
|
15
|
+
#
|
|
16
|
+
# @raise [SlackClient::Error] in case the Slack API returns an error
|
|
17
|
+
# other than `users_not_found`
|
|
18
|
+
#
|
|
19
|
+
# @return [Array] Slack response for each assignee a message was sent to
|
|
20
|
+
def dispatch
|
|
21
|
+
@assignees.each do |assignee|
|
|
22
|
+
dispatch_one(assignee)
|
|
23
|
+
end
|
|
24
|
+
end
|
|
25
|
+
|
|
26
|
+
# Make a Slack API call to dispatch the message to the user or channel
|
|
27
|
+
#
|
|
28
|
+
# @raise [SlackClient::Error] in case the Slack API returns an error
|
|
29
|
+
# other than `users_not_found`
|
|
30
|
+
#
|
|
31
|
+
# @param [String] the assignee handle string
|
|
32
|
+
# @return [Hash] the Slack response
|
|
33
|
+
def dispatch_one(assignee)
|
|
34
|
+
user = slack_user_or_channel(assignee)
|
|
35
|
+
|
|
36
|
+
client.post_message(user.dig("user", "id"), slack_message(user, assignee))
|
|
37
|
+
rescue SlackClient::Error => error
|
|
38
|
+
if ["users_not_found", "channel_not_found"].include?(error.error_code)
|
|
39
|
+
user = { "user" => { "id" => @options[:fallback_channel] }, "fallback" => true }
|
|
40
|
+
else
|
|
41
|
+
raise(error)
|
|
42
|
+
end
|
|
43
|
+
|
|
44
|
+
client.post_message(user.dig("user", "id"), slack_message(user, assignee))
|
|
45
|
+
end
|
|
46
|
+
|
|
47
|
+
private
|
|
48
|
+
|
|
49
|
+
# Returns a formatted hash containing either the user id of a slack user or
|
|
50
|
+
# the channel the message should be sent to.
|
|
51
|
+
#
|
|
52
|
+
# @return [Hash] a suited hash containing the user ID for a given individual or a slack channel
|
|
53
|
+
def slack_user_or_channel(assignee)
|
|
54
|
+
if assignee.include?("@")
|
|
55
|
+
client.lookup_user_by_email(assignee)
|
|
56
|
+
else
|
|
57
|
+
{ "user" => { "id" => assignee } }
|
|
58
|
+
end
|
|
59
|
+
end
|
|
60
|
+
|
|
61
|
+
# @return [SlackClient] an instance of SlackClient
|
|
62
|
+
def client
|
|
63
|
+
@client ||= SlackClient.new(@options[:slack_token])
|
|
64
|
+
end
|
|
65
|
+
end
|
|
66
|
+
end
|
|
67
|
+
end
|
|
@@ -0,0 +1,57 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
gem("bundler")
|
|
4
|
+
require "bundler"
|
|
5
|
+
|
|
6
|
+
module SmartTodo
|
|
7
|
+
module Events
|
|
8
|
+
# An event that compare the version of a gem specified in your Gemfile.lock
|
|
9
|
+
# with the expected version specifiers.
|
|
10
|
+
class GemBump
|
|
11
|
+
# @param gem_name [String]
|
|
12
|
+
# @param requirements [Array] a list of version specifiers.
|
|
13
|
+
# The specifiers are the same as the one used in Gemfiles or Gemspecs
|
|
14
|
+
#
|
|
15
|
+
# @example Expecting a specific version
|
|
16
|
+
# GemBump.new('rails', ['6.0'])
|
|
17
|
+
#
|
|
18
|
+
# @example Expecting a version in the 5.x.x series
|
|
19
|
+
# GemBump.new('rails', ['> 5.2', '< 6'])
|
|
20
|
+
def initialize(gem_name, requirements)
|
|
21
|
+
@gem_name = gem_name
|
|
22
|
+
@requirements = Gem::Requirement.new(requirements)
|
|
23
|
+
end
|
|
24
|
+
|
|
25
|
+
# @return [String, false]
|
|
26
|
+
def met?
|
|
27
|
+
return error_message if spec_set[@gem_name].empty?
|
|
28
|
+
|
|
29
|
+
installed_version = spec_set[@gem_name].first.version
|
|
30
|
+
if @requirements.satisfied_by?(installed_version)
|
|
31
|
+
message(installed_version)
|
|
32
|
+
else
|
|
33
|
+
false
|
|
34
|
+
end
|
|
35
|
+
end
|
|
36
|
+
|
|
37
|
+
# Error message send to Slack in case a gem couldn't be found
|
|
38
|
+
#
|
|
39
|
+
# @return [String]
|
|
40
|
+
def error_message
|
|
41
|
+
"The gem *#{@gem_name}* is not in your dependencies, I can't determine if your TODO is ready to be addressed."
|
|
42
|
+
end
|
|
43
|
+
|
|
44
|
+
# @return [String]
|
|
45
|
+
def message(version_number)
|
|
46
|
+
"The gem *#{@gem_name}* was updated to version *#{version_number}* and your TODO is now ready to be addressed."
|
|
47
|
+
end
|
|
48
|
+
|
|
49
|
+
private
|
|
50
|
+
|
|
51
|
+
# @return [Bundler::SpecSet] an instance of Bundler::SpecSet
|
|
52
|
+
def spec_set
|
|
53
|
+
@spec_set ||= Bundler.load.specs
|
|
54
|
+
end
|
|
55
|
+
end
|
|
56
|
+
end
|
|
57
|
+
end
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
# frozen_string_literal: true
|
|
2
2
|
|
|
3
|
-
require
|
|
4
|
-
require
|
|
3
|
+
require "net/http"
|
|
4
|
+
require "json"
|
|
5
5
|
|
|
6
6
|
module SmartTodo
|
|
7
7
|
module Events
|
|
@@ -30,7 +30,7 @@ module SmartTodo
|
|
|
30
30
|
if response.code_type < Net::HTTPClientError
|
|
31
31
|
error_message
|
|
32
32
|
elsif (gem = version_released?(response.body))
|
|
33
|
-
message(gem[
|
|
33
|
+
message(gem["number"])
|
|
34
34
|
else
|
|
35
35
|
false
|
|
36
36
|
end
|
|
@@ -54,13 +54,13 @@ module SmartTodo
|
|
|
54
54
|
# @return [true, false]
|
|
55
55
|
def version_released?(gem_versions)
|
|
56
56
|
JSON.parse(gem_versions).find do |gem|
|
|
57
|
-
@requirements.satisfied_by?(Gem::Version.new(gem[
|
|
57
|
+
@requirements.satisfied_by?(Gem::Version.new(gem["number"]))
|
|
58
58
|
end
|
|
59
59
|
end
|
|
60
60
|
|
|
61
61
|
# @return [Net::HTTP] an instance of Net::HTTP
|
|
62
62
|
def client
|
|
63
|
-
@client ||= Net::HTTP.new(
|
|
63
|
+
@client ||= Net::HTTP.new("rubygems.org", Net::HTTP.https_default_port).tap do |client|
|
|
64
64
|
client.use_ssl = true
|
|
65
65
|
end
|
|
66
66
|
end
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
# frozen_string_literal: true
|
|
2
2
|
|
|
3
|
-
require
|
|
4
|
-
require
|
|
3
|
+
require "net/http"
|
|
4
|
+
require "json"
|
|
5
5
|
|
|
6
6
|
module SmartTodo
|
|
7
7
|
module Events
|
|
@@ -12,7 +12,7 @@ module SmartTodo
|
|
|
12
12
|
# with the `repos` scope in the +SMART_TODO_GITHUB_TOKEN+ environment variable
|
|
13
13
|
# is required.
|
|
14
14
|
class IssueClose
|
|
15
|
-
TOKEN_ENV =
|
|
15
|
+
TOKEN_ENV = "SMART_TODO_GITHUB_TOKEN"
|
|
16
16
|
|
|
17
17
|
# @param organization [String]
|
|
18
18
|
# @param repo [String]
|
|
@@ -62,7 +62,7 @@ module SmartTodo
|
|
|
62
62
|
|
|
63
63
|
# @return [Net::HTTP] an instance of Net::HTTP
|
|
64
64
|
def client
|
|
65
|
-
@client ||= Net::HTTP.new(
|
|
65
|
+
@client ||= Net::HTTP.new("api.github.com", Net::HTTP.https_default_port).tap do |client|
|
|
66
66
|
client.use_ssl = true
|
|
67
67
|
end
|
|
68
68
|
end
|
|
@@ -72,13 +72,13 @@ module SmartTodo
|
|
|
72
72
|
#
|
|
73
73
|
# @return [true, false]
|
|
74
74
|
def pull_request_closed?(pull_request)
|
|
75
|
-
JSON.parse(pull_request)[
|
|
75
|
+
JSON.parse(pull_request)["state"] == "closed"
|
|
76
76
|
end
|
|
77
77
|
|
|
78
78
|
# @return [Hash]
|
|
79
79
|
def default_headers
|
|
80
|
-
{
|
|
81
|
-
headers[
|
|
80
|
+
{ "Accept" => "application/vnd.github.v3+json" }.tap do |headers|
|
|
81
|
+
headers["Authorization"] = "token #{ENV[TOKEN_ENV]}" if ENV[TOKEN_ENV]
|
|
82
82
|
end
|
|
83
83
|
end
|
|
84
84
|
end
|
data/lib/smart_todo/events.rb
CHANGED
|
@@ -38,6 +38,15 @@ module SmartTodo
|
|
|
38
38
|
GemRelease.new(gem_name, requirements).met?
|
|
39
39
|
end
|
|
40
40
|
|
|
41
|
+
# Check if +gem_name+ was bumped to the +requirements+ expected
|
|
42
|
+
#
|
|
43
|
+
# @param gem_name [String]
|
|
44
|
+
# @param requirements [Array<String>] a list of version specifiers
|
|
45
|
+
# @return [false, String]
|
|
46
|
+
def gem_bump(gem_name, *requirements)
|
|
47
|
+
GemBump.new(gem_name, requirements).met?
|
|
48
|
+
end
|
|
49
|
+
|
|
41
50
|
# Check if the issue +issue_number+ is closed
|
|
42
51
|
#
|
|
43
52
|
# @param organization [String] the GitHub organization name
|
|
@@ -45,7 +54,7 @@ module SmartTodo
|
|
|
45
54
|
# @param issue_number [String, Integer]
|
|
46
55
|
# @return [false, String]
|
|
47
56
|
def issue_close(organization, repo, issue_number)
|
|
48
|
-
IssueClose.new(organization, repo, issue_number, type:
|
|
57
|
+
IssueClose.new(organization, repo, issue_number, type: "issues").met?
|
|
49
58
|
end
|
|
50
59
|
|
|
51
60
|
# Check if the pull request +pr_number+ is closed
|
|
@@ -55,7 +64,7 @@ module SmartTodo
|
|
|
55
64
|
# @param pr_number [String, Integer]
|
|
56
65
|
# @return [false, String]
|
|
57
66
|
def pull_request_close(organization, repo, pr_number)
|
|
58
|
-
IssueClose.new(organization, repo, pr_number, type:
|
|
67
|
+
IssueClose.new(organization, repo, pr_number, type: "pulls").met?
|
|
59
68
|
end
|
|
60
69
|
end
|
|
61
70
|
end
|