prreview 0.2.0 → 0.4.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/.rubocop.yml +1 -0
- data/README.md +6 -1
- data/lib/prreview/version.rb +1 -1
- data/lib/prreview.rb +48 -41
- metadata +1 -1
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA256:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: 714f7802004de97bfd9e4defb9f447ad5b445ea77e12b8f04123218a6684549b
|
4
|
+
data.tar.gz: ce62752c1d1d11c13f432e0c74df032873088d75018a13dec4dfc658b5ed6c73
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: 7526eaadf9570e7ad2967a2e003989b3126bff0ae2d0907d4d4056f92b90f8e2e7dff91a17cef9cd8a6cd81625d20a0cf27dcb378a348f6e1f7708124952011f
|
7
|
+
data.tar.gz: b9a446c787e170bb3998211173e198d5cb67480512446ddbb9f0a7620a8233ec692f8493bed7c1e1b4a181968c8e6b1ee07f1a4838ddd515c300ef60ef18bddf
|
data/.rubocop.yml
CHANGED
data/README.md
CHANGED
@@ -1,3 +1,5 @@
|
|
1
|
+
[](https://badge.fury.io/rb/prreview)
|
2
|
+
|
1
3
|
## How to use
|
2
4
|
|
3
5
|
Install:
|
@@ -44,10 +46,13 @@ However, in the future we might add some optional integrations.
|
|
44
46
|
|
45
47
|
## Tips
|
46
48
|
|
49
|
+
- A well-written PR and linked issue description make a big difference — and are good practice anyway.
|
47
50
|
- Run `prreview` after you've thoroughly reviewed the PR. It works best when you understand the changes well.
|
48
51
|
- Don't hesitate to try different LLMs or refresh the response to see if something new comes up.
|
49
52
|
- Use `--all-content` and other extra options — they can significantly improve results for some PRs.
|
50
|
-
- After the LLM responds,
|
53
|
+
- After the LLM responds, to potentially uncover more issues:
|
54
|
+
- Ask "Anything else?".
|
55
|
+
- If the response has a list of problems, try responding to each of them, giving the LLM more context.
|
51
56
|
|
52
57
|
## Requirements
|
53
58
|
|
data/lib/prreview/version.rb
CHANGED
data/lib/prreview.rb
CHANGED
@@ -11,7 +11,7 @@ require 'optparse'
|
|
11
11
|
module Prreview
|
12
12
|
class CLI
|
13
13
|
DEFAULT_PROMPT = 'Your task is to review this pull request. Do you see any problems there?'
|
14
|
-
|
14
|
+
DEFAULT_LINKED_ISSUES_LIMIT = 5
|
15
15
|
|
16
16
|
# url or owner/repo#123 or #123
|
17
17
|
URL_REGEX = %r{
|
@@ -36,9 +36,9 @@ module Prreview
|
|
36
36
|
end
|
37
37
|
|
38
38
|
def process
|
39
|
-
fetch_pull_request
|
40
|
-
fetch_issues
|
41
39
|
load_optional_files
|
40
|
+
fetch_pull_request
|
41
|
+
fetch_linked_issues
|
42
42
|
build_xml
|
43
43
|
copy_result_to_clipboard
|
44
44
|
end
|
@@ -48,26 +48,31 @@ module Prreview
|
|
48
48
|
def parse_options!
|
49
49
|
@prompt = DEFAULT_PROMPT
|
50
50
|
@include_content = false
|
51
|
-
@
|
52
|
-
@optional_files
|
51
|
+
@linked_issues_limit = DEFAULT_LINKED_ISSUES_LIMIT
|
52
|
+
@optional_files = []
|
53
|
+
|
54
|
+
ARGV << '--help' if ARGV.empty?
|
53
55
|
|
54
|
-
|
55
|
-
|
56
|
+
OptionParser.new do |parser|
|
57
|
+
parser.banner = "Usage: #{File.basename($PROGRAM_NAME)} -u URL [options]"
|
56
58
|
|
57
|
-
|
58
|
-
|
59
|
-
|
60
|
-
|
61
|
-
|
59
|
+
parser.on('-u', '--url URL', 'Pull‑request URL (https://github.com/owner/repo/pull/1)') { |v| @url = v }
|
60
|
+
parser.on('-p', '--prompt PROMPT', 'Custom LLM prompt') { |v| @prompt = v }
|
61
|
+
parser.on('-a', '--all-content', 'Include full file contents') { @include_content = true }
|
62
|
+
parser.on('-l', '--limit LIMIT', Integer, "Limit number of issues fetched (default: #{DEFAULT_LINKED_ISSUES_LIMIT})") { |v| @linked_issues_limit = v }
|
63
|
+
parser.on('-o', '--optional PATHS', 'Comma‑separated paths to local files (relative or absolute, e.g. docs/description.md,/etc/hosts)') do |v|
|
62
64
|
@optional_files = v.split(',').map(&:strip)
|
63
65
|
end
|
64
|
-
|
65
|
-
puts
|
66
|
+
parser.on_tail('-v', '--version', 'Show version') do
|
67
|
+
puts VERSION
|
68
|
+
exit
|
69
|
+
end
|
70
|
+
parser.on_tail('-h', '--help', 'Show help') do
|
71
|
+
puts parser
|
66
72
|
exit
|
67
73
|
end
|
74
|
+
parser.parse!
|
68
75
|
end
|
69
|
-
|
70
|
-
parser.parse!
|
71
76
|
end
|
72
77
|
|
73
78
|
def parse_url!
|
@@ -84,11 +89,11 @@ module Prreview
|
|
84
89
|
|
85
90
|
def initialize_client
|
86
91
|
access_token = ENV.fetch('GITHUB_TOKEN', nil)
|
87
|
-
abort 'Error: GITHUB_TOKEN is not set' if access_token.to_s.empty?
|
92
|
+
abort 'Error: GITHUB_TOKEN is not set.' if access_token.to_s.empty?
|
88
93
|
|
89
94
|
@client = Octokit::Client.new(access_token:, auto_paginate: true)
|
90
95
|
rescue Octokit::Unauthorized
|
91
|
-
abort 'Error: Invalid GITHUB_TOKEN'
|
96
|
+
abort 'Error: Invalid GITHUB_TOKEN.'
|
92
97
|
end
|
93
98
|
|
94
99
|
def fetch_pull_request
|
@@ -107,53 +112,50 @@ module Prreview
|
|
107
112
|
end
|
108
113
|
|
109
114
|
def fetch_file_content(path)
|
110
|
-
puts "Fetching
|
115
|
+
puts "Fetching #{path}"
|
111
116
|
|
112
117
|
content = @client.contents(@full_repo, path:, ref: @pull_request.head.sha)
|
113
|
-
Base64.decode64(content[:content])
|
118
|
+
decoded = Base64.decode64(content[:content])
|
119
|
+
binary?(decoded) ? '(binary file)' : decoded
|
114
120
|
rescue Octokit::NotFound
|
115
121
|
'(file content not found)'
|
116
122
|
end
|
117
123
|
|
118
|
-
def
|
119
|
-
@
|
124
|
+
def fetch_linked_issues
|
125
|
+
@linked_issues = []
|
120
126
|
|
121
127
|
text = [@pull_request.body, *@comments].join("\n")
|
122
128
|
queue = extract_refs(text, URL_REGEX)
|
123
129
|
seen = Set.new
|
124
130
|
|
125
|
-
until queue.empty? || @
|
131
|
+
until queue.empty? || @linked_issues.length >= @linked_issues_limit
|
126
132
|
ref = queue.shift
|
127
133
|
key = ref[:key]
|
128
134
|
next if seen.include?(key)
|
129
135
|
|
130
136
|
seen << key
|
131
137
|
|
132
|
-
issue =
|
138
|
+
issue = fetch_linked_issue(ref)
|
133
139
|
next unless issue
|
134
140
|
|
135
|
-
@
|
141
|
+
@linked_issues << issue
|
136
142
|
|
137
143
|
new_text = [issue[:description], *issue[:comments]].join("\n")
|
138
144
|
new_refs = extract_refs(new_text, URL_REGEX).reject { |nref| seen.include?(nref[:key]) }
|
139
145
|
queue.concat(new_refs)
|
140
146
|
end
|
141
147
|
|
142
|
-
puts "Fetched #{@
|
148
|
+
puts "Fetched #{@linked_issues.length} linked issues (limit: #{@linked_issues_limit})"
|
143
149
|
end
|
144
150
|
|
145
151
|
def load_optional_files
|
146
152
|
@optional_file_contents = @optional_files.filter_map do |path|
|
147
|
-
|
148
|
-
|
149
|
-
|
150
|
-
|
151
|
-
warn "File #{path} not found, skipping"
|
152
|
-
nil
|
153
|
-
end
|
153
|
+
puts "Reading #{path}"
|
154
|
+
abort "Optional file #{path} not found." unless File.exist?(path)
|
155
|
+
content = File.read(path)
|
156
|
+
{ filename: path, content: }
|
154
157
|
rescue StandardError => e
|
155
|
-
|
156
|
-
nil
|
158
|
+
raise "Error reading file #{path}: #{e.message}"
|
157
159
|
end
|
158
160
|
end
|
159
161
|
|
@@ -171,11 +173,11 @@ module Prreview
|
|
171
173
|
end
|
172
174
|
end
|
173
175
|
|
174
|
-
def
|
176
|
+
def fetch_linked_issue(ref)
|
175
177
|
full_repo = "#{ref[:owner]}/#{ref[:repo]}"
|
176
178
|
number = ref[:number]
|
177
179
|
|
178
|
-
puts "Fetching issue ##{number} for #{full_repo}"
|
180
|
+
puts "Fetching linked issue ##{number} for #{full_repo}"
|
179
181
|
|
180
182
|
issue = @client.issue(full_repo, number)
|
181
183
|
{
|
@@ -186,7 +188,7 @@ module Prreview
|
|
186
188
|
comments: @client.issue_comments(full_repo, number).map(&:body)
|
187
189
|
}
|
188
190
|
rescue Octokit::NotFound
|
189
|
-
|
191
|
+
warn "Linked issue #{number} for #{full_repo} not found, skipping"
|
190
192
|
nil
|
191
193
|
end
|
192
194
|
|
@@ -194,6 +196,7 @@ module Prreview
|
|
194
196
|
builder = Nokogiri::XML::Builder.new(encoding: 'UTF-8') do |x|
|
195
197
|
x.prompt do
|
196
198
|
x.task @prompt
|
199
|
+
x.current_date DateTime.now
|
197
200
|
|
198
201
|
x.pull_request do
|
199
202
|
x.number @pr_number
|
@@ -212,8 +215,8 @@ module Prreview
|
|
212
215
|
end
|
213
216
|
end
|
214
217
|
|
215
|
-
@
|
216
|
-
x.
|
218
|
+
@linked_issues.each do |issue|
|
219
|
+
x.linked_issue do
|
217
220
|
x.repo issue[:full_repo]
|
218
221
|
x.number issue[:number]
|
219
222
|
x.title issue[:title]
|
@@ -241,9 +244,13 @@ module Prreview
|
|
241
244
|
@xml = builder.doc.root.to_xml
|
242
245
|
end
|
243
246
|
|
247
|
+
def binary?(string)
|
248
|
+
string.include?("\x00")
|
249
|
+
end
|
250
|
+
|
244
251
|
def copy_result_to_clipboard
|
245
252
|
Clipboard.copy(@xml)
|
246
|
-
puts 'XML prompt generated and copied to clipboard.'
|
253
|
+
puts 'XML prompt generated and copied to your clipboard.'
|
247
254
|
end
|
248
255
|
end
|
249
256
|
end
|