smart_todo 1.10.0 → 1.11.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/.github/workflows/build.yml +1 -1
- data/.github/workflows/rubocop.yml +1 -1
- data/Gemfile.lock +10 -10
- data/lib/smart_todo/cli.rb +29 -1
- data/lib/smart_todo/events.rb +32 -0
- data/lib/smart_todo/todo.rb +24 -0
- data/lib/smart_todo/version.rb +1 -1
- data/lib/smart_todo_comment_format_cop.rb +96 -0
- metadata +4 -3
checksums.yaml
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
---
|
|
2
2
|
SHA256:
|
|
3
|
-
metadata.gz:
|
|
4
|
-
data.tar.gz:
|
|
3
|
+
metadata.gz: cd76c16d18b12ed3caada5a661031dd5f14c60aa1b0b227c0eb720116755c2cc
|
|
4
|
+
data.tar.gz: 91dadbb687dcfd22c4152f2b38054873f6d64fce3b43d8d96b8ac6891947e92a
|
|
5
5
|
SHA512:
|
|
6
|
-
metadata.gz:
|
|
7
|
-
data.tar.gz:
|
|
6
|
+
metadata.gz: cede44f989ddbc2a4174c5c273e1e7e08521bbaaddc2a18fe5afc88d04fb84cdb6644e9d6c6839f6e7257dc1271f7f188bbdc6edec21a19debb8e0a438c20130
|
|
7
|
+
data.tar.gz: fc3d770921d2c2d7a275236ea068c9aef5f2202de990c644afeb4cfb974581e62e8ba77ae91bc6d6af265b438dde88b62052a9046f57301808a1a695d12e3c1c
|
data/.github/workflows/build.yml
CHANGED
data/Gemfile.lock
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
PATH
|
|
2
2
|
remote: .
|
|
3
3
|
specs:
|
|
4
|
-
smart_todo (1.
|
|
4
|
+
smart_todo (1.11.0)
|
|
5
5
|
prism (~> 1.0)
|
|
6
6
|
|
|
7
7
|
GEM
|
|
@@ -10,26 +10,26 @@ GEM
|
|
|
10
10
|
addressable (2.8.7)
|
|
11
11
|
public_suffix (>= 2.0.2, < 7.0)
|
|
12
12
|
ast (2.4.3)
|
|
13
|
-
bigdecimal (3.1
|
|
14
|
-
crack (1.0.
|
|
13
|
+
bigdecimal (3.3.1)
|
|
14
|
+
crack (1.0.1)
|
|
15
15
|
bigdecimal
|
|
16
16
|
rexml
|
|
17
|
-
hashdiff (1.1
|
|
17
|
+
hashdiff (1.2.1)
|
|
18
18
|
json (2.10.2)
|
|
19
19
|
language_server-protocol (3.17.0.4)
|
|
20
20
|
lint_roller (1.1.0)
|
|
21
|
-
minitest (5.
|
|
21
|
+
minitest (5.26.0)
|
|
22
22
|
parallel (1.26.3)
|
|
23
23
|
parser (3.3.7.3)
|
|
24
24
|
ast (~> 2.4.1)
|
|
25
25
|
racc
|
|
26
|
-
prism (1.
|
|
27
|
-
public_suffix (6.0.
|
|
26
|
+
prism (1.6.0)
|
|
27
|
+
public_suffix (6.0.2)
|
|
28
28
|
racc (1.8.1)
|
|
29
29
|
rainbow (3.1.1)
|
|
30
|
-
rake (13.
|
|
30
|
+
rake (13.3.0)
|
|
31
31
|
regexp_parser (2.10.0)
|
|
32
|
-
rexml (3.4.
|
|
32
|
+
rexml (3.4.4)
|
|
33
33
|
rubocop (1.74.0)
|
|
34
34
|
json (~> 2.3)
|
|
35
35
|
language_server-protocol (~> 3.17.0.2)
|
|
@@ -49,7 +49,7 @@ GEM
|
|
|
49
49
|
unicode-display_width (3.1.4)
|
|
50
50
|
unicode-emoji (~> 4.0, >= 4.0.4)
|
|
51
51
|
unicode-emoji (4.0.4)
|
|
52
|
-
webmock (3.
|
|
52
|
+
webmock (3.26.0)
|
|
53
53
|
addressable (>= 2.8.0)
|
|
54
54
|
crack (>= 0.3.2)
|
|
55
55
|
hashdiff (>= 0.4.0, < 2.0.0)
|
data/lib/smart_todo/cli.rb
CHANGED
|
@@ -106,12 +106,40 @@ module SmartTodo
|
|
|
106
106
|
end
|
|
107
107
|
|
|
108
108
|
@errors.concat(todo.errors)
|
|
109
|
-
|
|
109
|
+
|
|
110
|
+
next unless event_met
|
|
111
|
+
|
|
112
|
+
event_message = append_context_if_applicable(event_message, todo, event_met, events)
|
|
113
|
+
|
|
114
|
+
dispatches << [event_message, todo]
|
|
110
115
|
end
|
|
111
116
|
|
|
112
117
|
dispatches
|
|
113
118
|
end
|
|
114
119
|
|
|
120
|
+
private
|
|
121
|
+
|
|
122
|
+
# @param event_message [String] the original event message
|
|
123
|
+
# @param todo [Todo] the todo object that may contain context
|
|
124
|
+
# @param event [Event] the event that was met
|
|
125
|
+
# @param events [Events] the events instance for fetching issue context
|
|
126
|
+
# @return [String] the event message, potentially with context appended
|
|
127
|
+
def append_context_if_applicable(event_message, todo, event, events)
|
|
128
|
+
return event_message unless should_apply_context?(todo, event)
|
|
129
|
+
|
|
130
|
+
org, repo, issue_number = todo.context.arguments
|
|
131
|
+
context_message = events.issue_context(org, repo, issue_number)
|
|
132
|
+
|
|
133
|
+
context_message ? "#{event_message}\n\n#{context_message}" : event_message
|
|
134
|
+
end
|
|
135
|
+
|
|
136
|
+
# @param todo [Todo] the todo object to check for context
|
|
137
|
+
# @param event [Event] the event to check
|
|
138
|
+
# @return [Boolean] true if context should be applied, false otherwise
|
|
139
|
+
def should_apply_context?(todo, event)
|
|
140
|
+
!!todo.context
|
|
141
|
+
end
|
|
142
|
+
|
|
115
143
|
def process_dispatches(dispatches)
|
|
116
144
|
queue = Queue.new
|
|
117
145
|
dispatches.each { |dispatch| queue << dispatch }
|
data/lib/smart_todo/events.rb
CHANGED
|
@@ -75,6 +75,8 @@ module SmartTodo
|
|
|
75
75
|
false
|
|
76
76
|
end
|
|
77
77
|
end
|
|
78
|
+
rescue Net::HTTPError, JSON::ParserError
|
|
79
|
+
"Error retrieving gem information for *#{gem_name}*."
|
|
78
80
|
end
|
|
79
81
|
|
|
80
82
|
# Check if +gem_name+ was bumped to the +requirements+ expected
|
|
@@ -130,6 +132,8 @@ module SmartTodo
|
|
|
130
132
|
else
|
|
131
133
|
false
|
|
132
134
|
end
|
|
135
|
+
rescue Net::HTTPError, JSON::ParserError
|
|
136
|
+
"Error retrieving issue information for #{organization}/#{repo}##{issue_number}."
|
|
133
137
|
end
|
|
134
138
|
|
|
135
139
|
# Check if the pull request +pr_number+ is closed
|
|
@@ -155,6 +159,34 @@ module SmartTodo
|
|
|
155
159
|
else
|
|
156
160
|
false
|
|
157
161
|
end
|
|
162
|
+
rescue Net::HTTPError, JSON::ParserError
|
|
163
|
+
"Error retrieving pull request information for #{organization}/#{repo}##{pr_number}."
|
|
164
|
+
end
|
|
165
|
+
|
|
166
|
+
# Retrieve context information for an issue
|
|
167
|
+
# This is used when a TODO has a context: issue() attribute
|
|
168
|
+
#
|
|
169
|
+
# @param organization [String] the GitHub organization name
|
|
170
|
+
# @param repo [String] the GitHub repo name
|
|
171
|
+
# @param issue_number [String, Integer]
|
|
172
|
+
# @return [String, nil]
|
|
173
|
+
def issue_context(organization, repo, issue_number)
|
|
174
|
+
headers = github_headers(organization, repo)
|
|
175
|
+
response = github_client.get("/repos/#{organization}/#{repo}/issues/#{issue_number}", headers)
|
|
176
|
+
|
|
177
|
+
if response.code_type < Net::HTTPClientError
|
|
178
|
+
nil
|
|
179
|
+
else
|
|
180
|
+
issue = JSON.parse(response.body)
|
|
181
|
+
state = issue["state"]
|
|
182
|
+
title = issue["title"]
|
|
183
|
+
assignee = issue["assignee"] ? "@#{issue["assignee"]["login"]}" : "unassigned"
|
|
184
|
+
|
|
185
|
+
"📌 Context: Issue ##{issue_number} - \"#{title}\" [#{state}] (#{assignee}) - " \
|
|
186
|
+
"https://github.com/#{organization}/#{repo}/issues/#{issue_number}"
|
|
187
|
+
end
|
|
188
|
+
rescue Net::HTTPError, JSON::ParserError
|
|
189
|
+
nil
|
|
158
190
|
end
|
|
159
191
|
|
|
160
192
|
# Check if the installed ruby version meets requirements.
|
data/lib/smart_todo/todo.rb
CHANGED
|
@@ -4,6 +4,7 @@ module SmartTodo
|
|
|
4
4
|
class Todo
|
|
5
5
|
attr_reader :filepath, :comment, :indent
|
|
6
6
|
attr_reader :events, :assignees, :errors
|
|
7
|
+
attr_accessor :context
|
|
7
8
|
|
|
8
9
|
def initialize(source, filepath = "-e")
|
|
9
10
|
@filepath = filepath
|
|
@@ -12,6 +13,7 @@ module SmartTodo
|
|
|
12
13
|
|
|
13
14
|
@events = []
|
|
14
15
|
@assignees = []
|
|
16
|
+
@context = nil
|
|
15
17
|
@errors = []
|
|
16
18
|
|
|
17
19
|
parse(source[(indent + 1)..])
|
|
@@ -66,6 +68,28 @@ module SmartTodo
|
|
|
66
68
|
end
|
|
67
69
|
when :to
|
|
68
70
|
metadata.assignees << visit(element.value)
|
|
71
|
+
when :context
|
|
72
|
+
value = visit(element.value)
|
|
73
|
+
|
|
74
|
+
unless value.is_a?(String)
|
|
75
|
+
metadata.errors << "Incorrect `:context` format: expected string value"
|
|
76
|
+
next
|
|
77
|
+
end
|
|
78
|
+
|
|
79
|
+
unless value =~ %r{^([^/]+)/([^#]+)#(\d+)$}
|
|
80
|
+
metadata.errors << "Incorrect `:context` format: expected \"org/repo#issue_number\""
|
|
81
|
+
next
|
|
82
|
+
end
|
|
83
|
+
|
|
84
|
+
org = ::Regexp.last_match(1)
|
|
85
|
+
repo = ::Regexp.last_match(2)
|
|
86
|
+
issue_number = ::Regexp.last_match(3)
|
|
87
|
+
|
|
88
|
+
if org.empty? || repo.empty?
|
|
89
|
+
metadata.errors << "Incorrect `:context` format: org and repo cannot be empty"
|
|
90
|
+
else
|
|
91
|
+
metadata.context = CallNode.new(:issue, [org, repo, issue_number], element.value.location)
|
|
92
|
+
end
|
|
69
93
|
end
|
|
70
94
|
end
|
|
71
95
|
end
|
data/lib/smart_todo/version.rb
CHANGED
|
@@ -0,0 +1,96 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
require "smart_todo"
|
|
4
|
+
require "parser"
|
|
5
|
+
|
|
6
|
+
module RuboCop
|
|
7
|
+
module Cop
|
|
8
|
+
module SmartTodo
|
|
9
|
+
# A RuboCop cop to enforce proper formatting of SmartTodo comments.
|
|
10
|
+
# SmartTodo comments must have their description on separate lines, indented by 2 spaces.
|
|
11
|
+
#
|
|
12
|
+
# Bad:
|
|
13
|
+
# # TODO(on: date('2024-03-29'), to: 'john@example.com'): Remove this
|
|
14
|
+
# # TODO(on: date('2024-03-29'), to: 'john@example.com') Remove this
|
|
15
|
+
# # TODO(on: date('2024-03-29'), to: 'john@example.com')
|
|
16
|
+
# # Remove this (not indented)
|
|
17
|
+
#
|
|
18
|
+
# Good:
|
|
19
|
+
# # TODO(on: date('2024-03-29'), to: 'john@example.com')
|
|
20
|
+
# # Remove this (indented by 2 extra spaces)
|
|
21
|
+
#
|
|
22
|
+
class SmartTodoCommentFormatCop < Base
|
|
23
|
+
extend AutoCorrector
|
|
24
|
+
|
|
25
|
+
MSG_INLINE = "SmartTodo comment must not be on the same line as the TODO. " \
|
|
26
|
+
"For more info please look at https://github.com/Shopify/smart_todo/wiki/Syntax"
|
|
27
|
+
MSG_INDENT = "SmartTodo continuation line must be indented by 2 spaces. " \
|
|
28
|
+
"For more info please look at https://github.com/Shopify/smart_todo/wiki/Syntax"
|
|
29
|
+
|
|
30
|
+
SMART_TODO_PATTERN = /\A\s*#\s*(TODO|FIXME|OPTIMIZE)\(on:/
|
|
31
|
+
INLINE_TEXT_PATTERN = /\A(\s*#\s*(?:TODO|FIXME|OPTIMIZE)\(.+\))\s*:?\s+(.+)/
|
|
32
|
+
|
|
33
|
+
def on_new_investigation
|
|
34
|
+
processed_source.comments.each_with_index do |comment, index|
|
|
35
|
+
next unless smart_todo_comment?(comment)
|
|
36
|
+
|
|
37
|
+
check_inline_text(comment)
|
|
38
|
+
check_continuation_indent(comment, processed_source.comments[index + 1])
|
|
39
|
+
end
|
|
40
|
+
end
|
|
41
|
+
|
|
42
|
+
private
|
|
43
|
+
|
|
44
|
+
def smart_todo_comment?(comment)
|
|
45
|
+
comment.text.match?(SMART_TODO_PATTERN)
|
|
46
|
+
end
|
|
47
|
+
|
|
48
|
+
def check_inline_text(comment)
|
|
49
|
+
match = comment.text.match(INLINE_TEXT_PATTERN)
|
|
50
|
+
return unless match
|
|
51
|
+
|
|
52
|
+
add_offense(comment.location.expression, message: MSG_INLINE) do |corrector|
|
|
53
|
+
todo_part = match[1]
|
|
54
|
+
text_part = match[2]
|
|
55
|
+
indentation = " " * comment.location.column
|
|
56
|
+
|
|
57
|
+
corrected = "#{todo_part}\n#{indentation}# #{text_part}"
|
|
58
|
+
corrector.replace(comment.location.expression, corrected)
|
|
59
|
+
end
|
|
60
|
+
end
|
|
61
|
+
|
|
62
|
+
def check_continuation_indent(comment, next_comment)
|
|
63
|
+
return unless next_comment
|
|
64
|
+
return unless next_comment.location.line == comment.location.line + 1
|
|
65
|
+
return if smart_todo_comment?(next_comment)
|
|
66
|
+
return if empty_comment?(next_comment)
|
|
67
|
+
return if properly_indented?(next_comment)
|
|
68
|
+
|
|
69
|
+
add_offense(next_comment.location.expression, message: MSG_INDENT) do |corrector|
|
|
70
|
+
corrected = fix_indentation(next_comment.text)
|
|
71
|
+
corrector.replace(next_comment.location.expression, corrected)
|
|
72
|
+
end
|
|
73
|
+
end
|
|
74
|
+
|
|
75
|
+
def empty_comment?(comment)
|
|
76
|
+
comment.text.match?(/\A\s*#\s*\z/)
|
|
77
|
+
end
|
|
78
|
+
|
|
79
|
+
def properly_indented?(comment)
|
|
80
|
+
# A properly indented continuation has exactly 2 spaces after the #
|
|
81
|
+
comment.text.match?(/\A\s*# \S/)
|
|
82
|
+
end
|
|
83
|
+
|
|
84
|
+
def fix_indentation(text)
|
|
85
|
+
# Extract leading whitespace and content
|
|
86
|
+
match = text.match(/\A(\s*)#\s*(\S.*)\z/)
|
|
87
|
+
return text unless match
|
|
88
|
+
|
|
89
|
+
leading_space = match[1]
|
|
90
|
+
content = match[2]
|
|
91
|
+
"#{leading_space}# #{content}"
|
|
92
|
+
end
|
|
93
|
+
end
|
|
94
|
+
end
|
|
95
|
+
end
|
|
96
|
+
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.11.0
|
|
5
5
|
platform: ruby
|
|
6
6
|
authors:
|
|
7
7
|
- Shopify
|
|
8
8
|
bindir: exe
|
|
9
9
|
cert_chain: []
|
|
10
|
-
date:
|
|
10
|
+
date: 1980-01-02 00:00:00.000000000 Z
|
|
11
11
|
dependencies:
|
|
12
12
|
- !ruby/object:Gem::Dependency
|
|
13
13
|
name: prism
|
|
@@ -121,6 +121,7 @@ files:
|
|
|
121
121
|
- lib/smart_todo/slack_client.rb
|
|
122
122
|
- lib/smart_todo/todo.rb
|
|
123
123
|
- lib/smart_todo/version.rb
|
|
124
|
+
- lib/smart_todo_comment_format_cop.rb
|
|
124
125
|
- lib/smart_todo_cop.rb
|
|
125
126
|
- service.yml
|
|
126
127
|
- shipit.rubygems.yml
|
|
@@ -147,7 +148,7 @@ required_rubygems_version: !ruby/object:Gem::Requirement
|
|
|
147
148
|
- !ruby/object:Gem::Version
|
|
148
149
|
version: '0'
|
|
149
150
|
requirements: []
|
|
150
|
-
rubygems_version: 3.
|
|
151
|
+
rubygems_version: 3.7.2
|
|
151
152
|
specification_version: 4
|
|
152
153
|
summary: Enhance todo's comments in your codebase.
|
|
153
154
|
test_files: []
|