smart_todo 1.9.2 → 1.10.0
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/Gemfile.lock +1 -1
- data/lib/smart_todo/cli.rb +3 -0
- data/lib/smart_todo/comment_parser.rb +4 -1
- data/lib/smart_todo/dispatchers/base.rb +10 -1
- data/lib/smart_todo/dispatchers/slack.rb +2 -1
- data/lib/smart_todo/version.rb +1 -1
- data/lib/smart_todo_cop.rb +95 -9
- metadata +2 -2
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA256:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: eed299ced3a12723ccf8cb775ea33b3ce784b1c05c18ced890b3c8b184c0124e
|
4
|
+
data.tar.gz: a2e1ff5973fb8b1b7b40ee700a56acbee6c6c30df0c23e16e8c9505fe24af8ba
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: a36e6f20daabc1e3cdd74d8c5b63fd6284e0581de67b769dbeea5c97948f438cddefae166cf8c260d11237e6fbb22beeab9e0da1b0c069277f10c7d4ac1468ba
|
7
|
+
data.tar.gz: e020e58592618dd6567d8031dfe4c5c8c77bc7ccb372d7de2060c98f13ef072eaf31a01e16d2e9bc07118ea8553b2d716c95df19a0aed105b45baea1c6bf2406
|
data/Gemfile.lock
CHANGED
data/lib/smart_todo/cli.rb
CHANGED
@@ -66,6 +66,9 @@ module SmartTodo
|
|
66
66
|
opts.on("--dispatcher DISPATCHER") do |dispatcher|
|
67
67
|
@options[:dispatcher] = dispatcher
|
68
68
|
end
|
69
|
+
opts.on("--repo [REPO]", "Repository name to include in notifications") do |repo|
|
70
|
+
@options[:repo] = repo || File.basename(Dir.pwd)
|
71
|
+
end
|
69
72
|
end
|
70
73
|
end
|
71
74
|
|
@@ -2,6 +2,9 @@
|
|
2
2
|
|
3
3
|
module SmartTodo
|
4
4
|
class CommentParser
|
5
|
+
SUPPORTED_TAGS = ["TODO", "FIXME", "OPTIMIZE"].freeze
|
6
|
+
TAG_PATTERN = /^#\s(#{SUPPORTED_TAGS.join("|")})\(/
|
7
|
+
|
5
8
|
attr_reader :todos
|
6
9
|
|
7
10
|
def initialize
|
@@ -54,7 +57,7 @@ module SmartTodo
|
|
54
57
|
|
55
58
|
source = comment.location.slice
|
56
59
|
|
57
|
-
if source.match?(
|
60
|
+
if source.match?(TAG_PATTERN)
|
58
61
|
todos << current_todo if current_todo
|
59
62
|
current_todo = Todo.new(source, filepath)
|
60
63
|
elsif current_todo && (indent = source[/^#(\s*)/, 1].length) && (indent - current_todo.indent == 2)
|
@@ -68,7 +68,7 @@ module SmartTodo
|
|
68
68
|
<<~EOM
|
69
69
|
#{header}
|
70
70
|
|
71
|
-
You have an assigned TODO in the `#{@file}` file.
|
71
|
+
You have an assigned TODO in the `#{@file}` file#{repo}.
|
72
72
|
#{@event_message}
|
73
73
|
|
74
74
|
Here is the associated comment on your TODO:
|
@@ -91,6 +91,15 @@ module SmartTodo
|
|
91
91
|
def existing_user
|
92
92
|
"Hello :wave:,"
|
93
93
|
end
|
94
|
+
|
95
|
+
def repo
|
96
|
+
repo = @options[:repo]
|
97
|
+
return unless repo
|
98
|
+
|
99
|
+
unless repo.empty?
|
100
|
+
" in repository `#{repo}`"
|
101
|
+
end
|
102
|
+
end
|
94
103
|
end
|
95
104
|
end
|
96
105
|
end
|
@@ -7,7 +7,8 @@ module SmartTodo
|
|
7
7
|
class Slack < Base
|
8
8
|
class << self
|
9
9
|
def validate_options!(options)
|
10
|
-
options[:slack_token] ||= ENV.fetch("SMART_TODO_SLACK_TOKEN"
|
10
|
+
options[:slack_token] ||= ENV.fetch("SMART_TODO_SLACK_TOKEN", "")
|
11
|
+
raise(ArgumentError, "Missing :slack_token") if options[:slack_token].empty?
|
11
12
|
|
12
13
|
options.fetch(:fallback_channel) { raise(ArgumentError, "Missing :fallback_channel") }
|
13
14
|
end
|
data/lib/smart_todo/version.rb
CHANGED
data/lib/smart_todo_cop.rb
CHANGED
@@ -1,6 +1,7 @@
|
|
1
1
|
# frozen_string_literal: true
|
2
2
|
|
3
3
|
require "smart_todo"
|
4
|
+
require "date"
|
4
5
|
|
5
6
|
module RuboCop
|
6
7
|
module Cop
|
@@ -12,12 +13,20 @@ module RuboCop
|
|
12
13
|
class SmartTodoCop < Base
|
13
14
|
HELP = "For more info please look at https://github.com/Shopify/smart_todo/wiki/Syntax"
|
14
15
|
MSG = "Don't write regular TODO comments. Write SmartTodo compatible syntax comments. #{HELP}"
|
16
|
+
INVESTIGATED_TAGS = ::SmartTodo::CommentParser::SUPPORTED_TAGS +
|
17
|
+
::SmartTodo::CommentParser::SUPPORTED_TAGS.map(&:downcase)
|
18
|
+
TODO_PATTERN = /^#\s@?(#{INVESTIGATED_TAGS.join("|")})\b/
|
15
19
|
|
16
20
|
# @param processed_source [RuboCop::ProcessedSource]
|
17
21
|
# @return [void]
|
18
22
|
def on_new_investigation
|
19
23
|
processed_source.comments.each do |comment|
|
20
|
-
next unless
|
24
|
+
next unless (match = TODO_PATTERN.match(comment.text))
|
25
|
+
|
26
|
+
if match[1] != match[1].upcase
|
27
|
+
add_offense(comment)
|
28
|
+
next
|
29
|
+
end
|
21
30
|
|
22
31
|
metadata = metadata(comment.text)
|
23
32
|
|
@@ -25,10 +34,10 @@ module RuboCop
|
|
25
34
|
add_offense(comment, message: "Invalid TODO format: #{metadata.errors.join(", ")}. #{HELP}")
|
26
35
|
elsif !smart_todo?(metadata)
|
27
36
|
add_offense(comment)
|
28
|
-
elsif (methods = invalid_event_methods(metadata.events)).any?
|
29
|
-
add_offense(comment, message: "Invalid event method(s): #{methods.join(", ")}. #{HELP}")
|
30
37
|
elsif invalid_assignees(metadata.assignees).any?
|
31
38
|
add_offense(comment, message: "Invalid event assignee. This method only accepts strings. #{HELP}")
|
39
|
+
elsif (invalid_events = validate_events(metadata.events)).any?
|
40
|
+
add_offense(comment, message: "#{invalid_events.join(". ")}. #{HELP}")
|
32
41
|
end
|
33
42
|
end
|
34
43
|
end
|
@@ -49,17 +58,94 @@ module RuboCop
|
|
49
58
|
metadata.assignees.any?
|
50
59
|
end
|
51
60
|
|
52
|
-
# @param metadata [Array<SmartTodo::Parser::MethodNode>]
|
53
|
-
# @return [Array<String>]
|
54
|
-
def invalid_event_methods(events)
|
55
|
-
events.map(&:method_name).reject { |method| ::SmartTodo::Events.method_defined?(method) }
|
56
|
-
end
|
57
|
-
|
58
61
|
# @param assignees [Array]
|
59
62
|
# @return [Array]
|
60
63
|
def invalid_assignees(assignees)
|
61
64
|
assignees.reject { |assignee| assignee.is_a?(String) }
|
62
65
|
end
|
66
|
+
|
67
|
+
# @param events [Array<SmartTodo::Todo::CallNode>]
|
68
|
+
# @return [Array<String>]
|
69
|
+
def validate_events(events)
|
70
|
+
invalid_methods = events.map(&:method_name).reject { |method| ::SmartTodo::Events.method_defined?(method) }
|
71
|
+
return ["Invalid event method(s): #{invalid_methods.join(", ")}"] if invalid_methods.any?
|
72
|
+
|
73
|
+
events.map do |event|
|
74
|
+
send(validate_method(event.method_name), event.arguments)
|
75
|
+
end.compact
|
76
|
+
end
|
77
|
+
|
78
|
+
# @param event_type [Symbol]
|
79
|
+
# @return [String]
|
80
|
+
def validate_method(event_type)
|
81
|
+
"validate_#{event_type}_args"
|
82
|
+
end
|
83
|
+
|
84
|
+
# @param args [Array]
|
85
|
+
# @return [String, nil] Returns error message if date is invalid, nil if valid
|
86
|
+
def validate_date_args(args)
|
87
|
+
date = args.first
|
88
|
+
Date.parse(date)
|
89
|
+
nil
|
90
|
+
rescue ArgumentError, TypeError
|
91
|
+
"Invalid date format: #{date}"
|
92
|
+
end
|
93
|
+
|
94
|
+
# @param args [Array]
|
95
|
+
# @return [String, nil] Returns error message if arguments are invalid, nil if valid
|
96
|
+
def validate_issue_close_args(args)
|
97
|
+
validate_fixed_arity_args(args, 3, "issue_close", ["organization", "repo", "issue_number"])
|
98
|
+
end
|
99
|
+
|
100
|
+
# @param args [Array]
|
101
|
+
# @return [String, nil] Returns error message if arguments are invalid, nil if valid
|
102
|
+
def validate_pull_request_close_args(args)
|
103
|
+
validate_fixed_arity_args(args, 3, "pull_request_close", ["organization", "repo", "pr_number"])
|
104
|
+
end
|
105
|
+
|
106
|
+
# @param args [Array]
|
107
|
+
# @return [String, nil] Returns error message if arguments are invalid, nil if valid
|
108
|
+
def validate_gem_release_args(args)
|
109
|
+
validate_gem_args(args, "gem_release")
|
110
|
+
end
|
111
|
+
|
112
|
+
# @param args [Array]
|
113
|
+
# @return [String, nil] Returns error message if arguments are invalid, nil if valid
|
114
|
+
def validate_gem_bump_args(args)
|
115
|
+
validate_gem_args(args, "gem_bump")
|
116
|
+
end
|
117
|
+
|
118
|
+
# @param args [Array]
|
119
|
+
# @return [String, nil] Returns error message if arguments are invalid, nil if valid
|
120
|
+
def validate_ruby_version_args(args)
|
121
|
+
if args.empty?
|
122
|
+
"Invalid ruby_version event: Expected at least 1 argument (version requirement), got 0"
|
123
|
+
elsif !args.all? { |arg| arg.is_a?(String) }
|
124
|
+
"Invalid ruby_version event: Version requirements must be strings"
|
125
|
+
end
|
126
|
+
end
|
127
|
+
|
128
|
+
# Helper method for validating fixed arity events
|
129
|
+
def validate_fixed_arity_args(args, expected_count, event_name, arg_names)
|
130
|
+
if args.size != expected_count
|
131
|
+
message = "Invalid #{event_name} event: Expected #{expected_count} arguments "
|
132
|
+
message += "(#{arg_names.join(", ")}), got #{args.size}"
|
133
|
+
message
|
134
|
+
elsif !args.all? { |arg| arg.is_a?(String) }
|
135
|
+
"Invalid #{event_name} event: Arguments must be strings"
|
136
|
+
end
|
137
|
+
end
|
138
|
+
|
139
|
+
# Helper method for validating gem-related events
|
140
|
+
def validate_gem_args(args, event_name)
|
141
|
+
if args.empty?
|
142
|
+
"Invalid #{event_name} event: Expected at least 1 argument (gem_name), got 0"
|
143
|
+
elsif !args[0].is_a?(String)
|
144
|
+
"Invalid #{event_name} event: First argument (gem_name) must be a string"
|
145
|
+
elsif args.size > 1 && !args[1..].all? { |arg| arg.is_a?(String) }
|
146
|
+
"Invalid #{event_name} event: Version requirements must be strings"
|
147
|
+
end
|
148
|
+
end
|
63
149
|
end
|
64
150
|
end
|
65
151
|
end
|
metadata
CHANGED
@@ -1,13 +1,13 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: smart_todo
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 1.
|
4
|
+
version: 1.10.0
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- Shopify
|
8
8
|
bindir: exe
|
9
9
|
cert_chain: []
|
10
|
-
date: 2025-
|
10
|
+
date: 2025-04-08 00:00:00.000000000 Z
|
11
11
|
dependencies:
|
12
12
|
- !ruby/object:Gem::Dependency
|
13
13
|
name: prism
|