Skip to content

Display line diffs in dsl --verify error messages#2592

Open
dejmedus wants to merge 1 commit intomainfrom
jb-add-diff-output-to-error-msg
Open

Display line diffs in dsl --verify error messages#2592
dejmedus wants to merge 1 commit intomainfrom
jb-add-diff-output-to-error-msg

Conversation

@dejmedus
Copy link
Copy Markdown
Contributor

@dejmedus dejmedus commented Apr 10, 2026

Motivation

When tapioca 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 visualize these changes by including the line diff output in the error message

Output example tapioca-verify-diff

Implementation

This PR diffs updated files against the originals during RBI verification and then includes the output in the error message when the diff is 250 lines or less, or whatever value is provided to --max-diff-lines flag

Tests

  • updated verify tests to include line diff
  • added a test for multi file changes and changes longer than max diff lines
  • added tests for diff behaviour

@dejmedus dejmedus added the enhancement New feature or request label Apr 10, 2026
@dejmedus dejmedus force-pushed the jb-add-diff-output-to-error-msg branch 3 times, most recently from f3c5b51 to 94ecc33 Compare April 21, 2026 19:31
@dejmedus dejmedus marked this pull request as ready for review April 21, 2026 20:46
@dejmedus dejmedus requested a review from a team as a code owner April 21, 2026 20:46
Comment thread lib/tapioca/commands/abstract_dsl.rb
Comment thread lib/tapioca/commands/abstract_dsl.rb Outdated
Comment thread lib/tapioca/commands/abstract_dsl.rb Outdated
end.join("\n")

raise Tapioca::Error, <<~ERROR
file_diff = if diff_output.lines.count.between?(1, 250)
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Should this 250 limit be a CLI option instead and default to 250? dsl --verify --max-diff-lines=1000

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Great idea 👌🏼

Comment thread lib/tapioca/commands/abstract_dsl.rb Outdated

stdout
rescue => e
say_error("Failed to create #{filename} diff. #{e.message}", :yellow)
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Why did you opt for yellow and not red? I think red is more consistent with rest of our errors.

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I looked through say_error uses and it seemed like :yellow was used for warnings and :red for errors. I leaned towards this being a warning. Makes sense, changed to :red :)

Comment thread spec/tapioca/cli/dsl_spec.rb
@command = DslVerify.new(
requested_constants: [],
requested_paths: [],
outpath: Pathname.new("tmp"),
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Does DslVerify write something to the file system? I think other DslVerify tests run on a temporary directory that's cleaned up but this is not an integration test so it shouldn't use that, it's more costly.

If it is writing something we should clean up after, maybe we can create @tmp = Dir.mktmpdir in the before block, use that as the outpath and then clean it up in an after block.

Copy link
Copy Markdown
Contributor Author

@dejmedus dejmedus Apr 23, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

If im understanding correctly, outpath is required to create DslVerify but the command isn't being executed here, I just need an instance to pull file_diff from. I changed it to /dev/null to make it clearer that its unused. tbh I wouldn't be that against removing this test file if we don't find it helpful, file diff is being tested by verify tests also

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actually ignore above, I moved out file_diff to rbi_files_helpers and the tests look much nicer now :)

@dejmedus dejmedus force-pushed the jb-add-diff-output-to-error-msg branch 3 times, most recently from 0aa9400 to 6bdba8a Compare April 24, 2026 21:11
@dejmedus dejmedus requested a review from KaanOzkan April 24, 2026 23:57
Copy link
Copy Markdown
Contributor

@KaanOzkan KaanOzkan left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Sorry I'm suggesting a refactor that I didn't noticed before.

diff_lines = diff_output.lines.count
file_diff = if diff_lines.between?(1, @max_diff_lines)
"#{set_color("Diff:", :red)}\n#{diff_output.chomp}"
elsif diff_lines > @max_diff_lines
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Instead of skipping it completely should we display the diff until @max_diff_lines?

Comment thread lib/tapioca/helpers/rbi_files_helper.rb Outdated
old_path.to_s,
new_path.to_s,
)
Kernel.raise stderr.chomp unless [0, 1].include?(status.exitstatus)
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Using exceptions for control flow is frowned upon and slow. So we shouldn't rely on it when we can just call say_error here instead. If Open3 itself can raise while running keep the rescue block too.

Suggested change
Kernel.raise stderr.chomp unless [0, 1].include?(status.exitstatus)
say_error("Failed to create #{filename} diff. #{e.message}", :red) unless [0, 1].include?(status.exitstatus)

Copy link
Copy Markdown
Contributor Author

@dejmedus dejmedus Apr 30, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Using exceptions for control flow is frowned upon and slow

Noted, thank you !

Comment thread lib/tapioca/commands/abstract_dsl.rb Outdated
#: (Pathname dir) -> void
def perform_dsl_verification(dir)
diff = verify_dsl_rbi(tmp_dir: dir)
diff, diff_output = verify_dsl_rbi(tmp_dir: dir)
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I don't love the tuple return here and I also forgot about report_diff_and_exit_if_out_of_date. What do you think about moving the calculation of diff output to report_diff_and_exit_if_out_of_date? It's the same class but it'll move some logic away from perform_dsl_verification/verify_dsl_rbi and make it easier to read. Something like this:

  #: (Hash[String, Symbol] diff, tmp_dir: Pathname, command: Symbol) -> void
  def report_diff_and_exit_if_out_of_date(diff, tmp_dir:, command:)
    if diff.empty?
      say("Nothing to do, all RBIs are up-to-date.")
      return
    end

    reasons = diff.group_by(&:last).sort.map do |cause, diff_for_cause|
      build_error_for_files(cause, diff_for_cause.map(&:first))
    end.join("\n")

    diff_output = build_diff_output(diff, tmp_dir)
    diff_lines  = diff_output.count("\n")

    diff_section =
      if diff_lines.between?(1, @max_diff_lines)
        "#{set_color("Diff:", :red)}\n#{diff_output.chomp}"
      elsif diff_lines > @max_diff_lines
        set_color("Diff not displayed as it exceeds #{@max_diff_lines} lines", :red)
      else
        ""
      end

    raise Tapioca::Error, <<~ERROR.rstrip
      #{set_color("RBI files are out-of-date. In your development environment, please run:", :green)}
        #{set_color("`#{default_command(command)}`", :green, :bold)}
      #{set_color("Once it is complete, be sure to commit and push any changes", :green)}
      If you don't observe any changes after running the command locally, ensure your database is in a good
      state e.g. run `bin/rails db:reset`

      #{set_color("Reason:", :red)}
      #{reasons}

      #{diff_section}
    ERROR
  end

  #: (Hash[String, Symbol] diff, Pathname tmp_dir) -> String
  def build_diff_output(diff, tmp_dir)
    out = String.new
    line_count = 0

    diff.each do |file, status|
      chunk = case status
      when :added   then file_diff(file, File::NULL, tmp_dir / file)
      when :removed then file_diff(file, @outpath / file, File::NULL)
      when :changed then file_diff(file, @outpath / file, tmp_dir / file)
      end

      out << chunk
      line_count += chunk.count("\n")
      break if line_count > @max_diff_lines
    end

    out
  end

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Agree, changed. Thanks!

def initialize(name)
Minitest::Runnable.instance_method(:initialize).bind(self).call(name)
end
end
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Instead of this could we get away with defining our own say_error method? It's simpler if that's all we need.

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

We could, but we'd have to turn off type check in the file since files helpers require Thor as ancestor

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
@dejmedus dejmedus force-pushed the jb-add-diff-output-to-error-msg branch from 6bdba8a to 85ec455 Compare April 30, 2026 16:50
@dejmedus dejmedus requested a review from KaanOzkan April 30, 2026 18:01
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

enhancement New feature or request

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants