Skip to content

Commit 85ec455

Browse files
committed
Display line diffs in dsl --verify error messages
When `dsl --verify` detects out of date RBI files, the error message lists which files were added, removed, or changed. We could make it easier to understand what has changed by including the line diff output in the error message as well This commit grabs the line diff output during RBI verification and includes it in the error message when the diff is 250 lines or less
1 parent 5e3ad72 commit 85ec455

7 files changed

Lines changed: 407 additions & 21 deletions

File tree

README.md

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -502,6 +502,8 @@ Options:
502502
-w, [--workers=N] # Number of parallel workers to use when generating RBIs (default: auto)
503503
[--rbi-max-line-length=N] # Set the max line length of generated RBIs. Signatures longer than the max line length will be wrapped
504504
# Default: 120
505+
[--max-diff-lines=N] # Max number of diff lines to include in the `dsl --verify` output
506+
# Default: 250
505507
-e, [--environment=ENVIRONMENT] # The Rack/Rails environment to use when generating RBIs
506508
# Default: development
507509
-l, [--list-compilers], [--no-list-compilers], [--skip-list-compilers] # List all loaded compilers
@@ -960,6 +962,7 @@ dsl:
960962
quiet: false
961963
workers: 1
962964
rbi_max_line_length: 120
965+
max_diff_lines: 250
963966
environment: development
964967
list_compilers: false
965968
app_root: "."

lib/tapioca.rb

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -32,6 +32,7 @@ class Error < StandardError; end
3232

3333
DEFAULT_RBI_MAX_LINE_LENGTH = 120
3434
DEFAULT_ENVIRONMENT = "development"
35+
DEFAULT_MAX_DIFF_LINES = 250
3536

3637
CENTRAL_REPO_ROOT_URI = "https://raw.githubusercontent.com/Shopify/rbi-central/main"
3738
CENTRAL_REPO_INDEX_PATH = "index.json"

lib/tapioca/cli.rb

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -116,6 +116,10 @@ def todo
116116
type: :numeric,
117117
desc: "Set the max line length of generated RBIs. Signatures longer than the max line length will be wrapped",
118118
default: DEFAULT_RBI_MAX_LINE_LENGTH
119+
option :max_diff_lines,
120+
type: :numeric,
121+
desc: "Max number of diff lines to include in the `dsl --verify` output",
122+
default: DEFAULT_MAX_DIFF_LINES
119123
option :environment,
120124
aliases: ["-e"],
121125
type: :string,
@@ -166,6 +170,7 @@ def dsl(*constant_or_paths)
166170
halt_upon_load_error: options[:halt_upon_load_error],
167171
compiler_options: options[:compiler_options],
168172
lsp_addon: self.class.addon_mode,
173+
max_diff_lines: options[:max_diff_lines],
169174
}
170175

171176
command = if options[:verify]

lib/tapioca/commands/abstract_dsl.rb

Lines changed: 62 additions & 18 deletions
Original file line numberDiff line numberDiff line change
@@ -26,7 +26,8 @@ class AbstractDsl < CommandWithoutTracker
2626
#| ?app_root: String,
2727
#| ?halt_upon_load_error: bool,
2828
#| ?compiler_options: Hash[String, untyped],
29-
#| ?lsp_addon: bool
29+
#| ?lsp_addon: bool,
30+
#| ?max_diff_lines: Integer
3031
#| ) -> void
3132
def initialize(
3233
requested_constants:,
@@ -46,7 +47,8 @@ def initialize(
4647
app_root: ".",
4748
halt_upon_load_error: true,
4849
compiler_options: {},
49-
lsp_addon: false
50+
lsp_addon: false,
51+
max_diff_lines: DEFAULT_MAX_DIFF_LINES
5052
)
5153
@requested_constants = requested_constants
5254
@requested_paths = requested_paths
@@ -66,6 +68,7 @@ def initialize(
6668
@skip_constant = skip_constant
6769
@compiler_options = compiler_options
6870
@lsp_addon = lsp_addon
71+
@max_diff_lines = max_diff_lines
6972

7073
super()
7174
end
@@ -242,7 +245,7 @@ def compile_dsl_rbi(constant_name, rbi, outpath: @outpath, quiet: false)
242245
def perform_dsl_verification(dir)
243246
diff = verify_dsl_rbi(tmp_dir: dir)
244247

245-
report_diff_and_exit_if_out_of_date(diff, :dsl)
248+
report_diff_and_exit_if_out_of_date(diff, tmp_dir: dir, command: :dsl)
246249
ensure
247250
FileUtils.remove_entry(dir)
248251
end
@@ -305,26 +308,67 @@ def build_error_for_files(cause, files)
305308
" File(s) #{cause}:\n - #{filenames}"
306309
end
307310

308-
#: (Hash[String, Symbol] diff, Symbol command) -> void
309-
def report_diff_and_exit_if_out_of_date(diff, command)
311+
#: (Hash[String, Symbol] diff, tmp_dir: Pathname, command: Symbol) -> void
312+
def report_diff_and_exit_if_out_of_date(diff, tmp_dir:, command:)
310313
if diff.empty?
311314
say("Nothing to do, all RBIs are up-to-date.")
312-
else
313-
reasons = diff.group_by(&:last).sort.map do |cause, diff_for_cause|
314-
build_error_for_files(cause, diff_for_cause.map(&:first))
315-
end.join("\n")
315+
return
316+
end
317+
318+
reasons = diff.group_by(&:last).sort.map do |cause, diff_for_cause|
319+
build_error_for_files(cause, diff_for_cause.map(&:first))
320+
end.join("\n")
321+
322+
diff_output = build_diff_output(diff, tmp_dir)
323+
diff_lines = diff_output.count("\n")
324+
325+
diff_section =
326+
if diff_lines.between?(1, @max_diff_lines)
327+
"#{set_color("Diff:", :red)}\n#{diff_output.chomp}"
328+
elsif diff_lines > @max_diff_lines
329+
truncated_output = diff_output.lines.first(@max_diff_lines).join
330+
"#{set_color("Diff truncated to #{@max_diff_lines} lines:", :red)}\n#{truncated_output.rstrip}"
331+
else
332+
""
333+
end
334+
335+
raise Tapioca::Error, <<~ERROR.rstrip
336+
#{set_color("RBI files are out-of-date. In your development environment, please run:", :green)}
337+
#{set_color("`#{default_command(command)}`", :green, :bold)}
338+
#{set_color("Once it is complete, be sure to commit and push any changes", :green)}
339+
If you don't observe any changes after running the command locally, ensure your database is in a good
340+
state e.g. run `bin/rails db:reset`
316341
317-
raise Tapioca::Error, <<~ERROR
318-
#{set_color("RBI files are out-of-date. In your development environment, please run:", :green)}
319-
#{set_color("`#{default_command(command)}`", :green, :bold)}
320-
#{set_color("Once it is complete, be sure to commit and push any changes", :green)}
321-
If you don't observe any changes after running the command locally, ensure your database is in a good
322-
state e.g. run `bin/rails db:reset`
342+
#{set_color("Reason:", :red)}
343+
#{reasons}
323344
324-
#{set_color("Reason:", :red)}
325-
#{reasons}
326-
ERROR
345+
#{diff_section}
346+
ERROR
347+
end
348+
349+
#: (Hash[String, Symbol] diff, Pathname tmp_dir) -> String
350+
def build_diff_output(diff, tmp_dir)
351+
out = String.new
352+
line_count = 0
353+
354+
diff.each do |file, status|
355+
filename = file.to_s
356+
old_path = (@outpath / file).to_s
357+
new_path = (tmp_dir / file).to_s
358+
359+
chunk = case status
360+
when :added then file_diff(filename, File::NULL, new_path)
361+
when :removed then file_diff(filename, old_path, File::NULL)
362+
when :changed then file_diff(filename, old_path, new_path)
363+
else ""
364+
end
365+
366+
out << chunk
367+
line_count += chunk.count("\n")
368+
break if line_count > @max_diff_lines
327369
end
370+
371+
out
328372
end
329373

330374
#: (Pathname path) -> Array[Pathname]

lib/tapioca/helpers/rbi_files_helper.rb

Lines changed: 26 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,8 @@
11
# typed: strict
22
# frozen_string_literal: true
33

4+
require "open3"
5+
46
module Tapioca
57
# @requires_ancestor: Thor::Shell
68
# @requires_ancestor: SorbetHelper
@@ -137,6 +139,30 @@ def validate_rbi_files(command:, gem_dir:, dsl_dir:, auto_strictness:, gems: [],
137139
Kernel.raise Tapioca::Error, error_messages.join("\n") if parse_errors.any?
138140
end
139141

142+
#: (String filename, String old_path, String new_path) -> String
143+
def file_diff(filename, old_path, new_path)
144+
stdout, stderr, status = Open3.capture3(
145+
"diff",
146+
"-u",
147+
"--label",
148+
filename,
149+
"--label",
150+
filename,
151+
old_path,
152+
new_path,
153+
)
154+
155+
unless [0, 1].include?(status.exitstatus)
156+
say_error("Failed to create #{filename} diff. #{stderr.chomp}", :red)
157+
return ""
158+
end
159+
160+
stdout
161+
rescue => e
162+
say_error("Failed to create #{filename} diff. #{e.message}", :red)
163+
""
164+
end
165+
140166
private
141167

142168
#: (RBI::Index index, Array[String] files, number_of_workers: Integer?) -> void

0 commit comments

Comments
 (0)