Skip to content

Commit

Permalink
wip
Browse files Browse the repository at this point in the history
  • Loading branch information
andyw8 committed Dec 19, 2024
1 parent 3ab954c commit cb3342c
Show file tree
Hide file tree
Showing 3 changed files with 180 additions and 84 deletions.
85 changes: 1 addition & 84 deletions lib/ruby_lsp/tapioca/addon.rb
Original file line number Diff line number Diff line change
Expand Up @@ -13,7 +13,6 @@
end

require "zlib"
require "ruby_lsp/tapioca/lockfile_diff_parser"

module RubyLsp
module Tapioca
Expand Down Expand Up @@ -48,9 +47,7 @@ def activate(global_state, outgoing_queue)
@outgoing_queue << Notification.window_log_message("Activating Tapioca add-on v#{version}")
@rails_runner_client.register_server_addon(File.expand_path("server_addon.rb", __dir__))

if git_repo?
lockfile_changed? ? generate_gem_rbis : cleanup_orphaned_rbis
end
RunGemRbiCheck.new.run
rescue IncompatibleApiError
# The requested version for the Rails add-on no longer matches. We need to upgrade and fix the breaking
# changes
Expand Down Expand Up @@ -133,86 +130,6 @@ def file_updated?(change, path)

false
end

sig { returns(T::Boolean) }
def git_repo?
require "open3"

_, status = Open3.capture2e("git rev-parse --is-inside-work-tree")

T.must(status.success?)
end

sig { returns(T::Boolean) }
def lockfile_changed?
fetch_lockfile_diff
!T.must(@lockfile_diff).empty?
end

sig { returns(String) }
def fetch_lockfile_diff
@lockfile_diff = %x(git diff HEAD Gemfile.lock).strip
end

sig { void }
def generate_gem_rbis
parser = LockfileDiffParser.new(@lockfile_diff)

removed_gems = parser.removed_gems
added_or_modified_gems = parser.added_or_modified_gems

if added_or_modified_gems.any?
# Resetting BUNDLE_GEMFILE to root folder to use the project's Gemfile instead of Ruby LSP's composed Gemfile
stdout, stderr, status = T.unsafe(Open3).capture3(
{ "BUNDLE_GEMFILE" => "Gemfile" },
"bin/tapioca",
"gem",
"--lsp_addon",
*added_or_modified_gems,
)
T.must(@outgoing_queue) << if status.success?
Notification.window_log_message(
stdout,
type: Constant::MessageType::INFO,
)
else
Notification.window_log_message(
stderr,
type: Constant::MessageType::ERROR,
)
end
elsif removed_gems.any?
FileUtils.rm_f(Dir.glob("sorbet/rbi/gems/{#{removed_gems.join(",")}}@*.rbi"))
T.must(@outgoing_queue) << Notification.window_log_message(
"Removed RBIs for: #{removed_gems.join(", ")}",
type: Constant::MessageType::INFO,
)
end
end

sig { void }
def cleanup_orphaned_rbis
untracked_files = %x(git ls-files --others --exclude-standard sorbet/rbi/gems/).lines.map(&:strip)
deleted_files = %x(git ls-files --deleted sorbet/rbi/gems/).lines.map(&:strip)

untracked_files.each do |file|
File.delete(file)

T.must(@outgoing_queue) << Notification.window_log_message(
"Deleted untracked RBI: #{file}",
type: Constant::MessageType::INFO,
)
end

deleted_files.each do |file|
%x(git checkout -- #{file})

T.must(@outgoing_queue) << Notification.window_log_message(
"Restored deleted RBI: #{file}",
type: Constant::MessageType::INFO,
)
end
end
end
end
end
115 changes: 115 additions & 0 deletions lib/ruby_lsp/tapioca/run_gem_rbi_check.rb
Original file line number Diff line number Diff line change
@@ -0,0 +1,115 @@
# typed: true

require "ruby_lsp/tapioca/lockfile_diff_parser"
require "ruby_lsp/tapioca/run_gem_rbi_check"

module RubyLsp
module Tapioca
# TODO: this could probably have a better name
class RunGemRbiCheck
extend T::Sig

def initialize
# We considered using logs to assert against things that other wise would be tricky to test directly,
# but it may not be needed.
@logs = []
end

attr_reader :logs

def run(project_path = ".")
FileUtils.chdir(project_path) do
if git_repo?
lockfile_changed? ? generate_gem_rbis : cleanup_orphaned_rbis
else
@logs << "Not a git repository"
end
end
end

private

sig { returns(T::Boolean) }
def git_repo?
require "open3"

_, status = Open3.capture2e("git rev-parse --is-inside-work-tree")

T.must(status.success?)
end

sig { returns(T::Boolean) }
def lockfile_changed?
fetch_lockfile_diff
!T.must(@lockfile_diff).empty?
end

sig { returns(String) }
def fetch_lockfile_diff
@lockfile_diff = %x(git diff HEAD Gemfile.lock).strip
end

sig { void }
def generate_gem_rbis
parser = Tapioca::LockfileDiffParser.new(@lockfile_diff)

removed_gems = parser.removed_gems
added_or_modified_gems = parser.added_or_modified_gems

if added_or_modified_gems.any?
# Resetting BUNDLE_GEMFILE to root folder to use the project's Gemfile instead of Ruby LSP's composed Gemfile
stdout, stderr, status = T.unsafe(Open3).capture3(
{ "BUNDLE_GEMFILE" => "Gemfile" },
"bin/tapioca",
"gem",
"--lsp_addon",
*added_or_modified_gems,
)
@logs << stdout
@logs << stderr
# T.must(@outgoing_queue) << if status.success?
# Notification.window_log_message(
# stdout,
# type: Constant::MessageType::INFO,
# )
# else
# Notification.window_log_message(
# stderr,
# type: Constant::MessageType::ERROR,
# )
# end
elsif removed_gems.any?
FileUtils.rm_f(Dir.glob("sorbet/rbi/gems/{#{removed_gems.join(",")}}@*.rbi"))
# T.must(@outgoing_queue) << RubyLsp::Notification.window_log_message(
# "Removed RBIs for: #{removed_gems.join(", ")}",
# type: LanguageServer::Protocol::Constant::MessageType::INFO,
# )
end
end

sig { void }
def cleanup_orphaned_rbis
untracked_files = %x(git ls-files --others --exclude-standard sorbet/rbi/gems/).lines.map(&:strip)
deleted_files = %x(git ls-files --deleted sorbet/rbi/gems/).lines.map(&:strip)

untracked_files.each do |file|
File.delete(file)

T.must(@outgoing_queue) << Notification.window_log_message(
"Deleted untracked RBI: #{file}",
type: Constant::MessageType::INFO,
)
end

deleted_files.each do |file|
%x(git checkout -- #{file})

T.must(@outgoing_queue) << RubyLsp::Notification.window_log_message(
"Restored deleted RBI: #{file}",
type: Constant::MessageType::INFO,
)
end
end
end
end
end
64 changes: 64 additions & 0 deletions spec/tapioca/ruby_lsp/run_gem_rbi_check_spec.rb
Original file line number Diff line number Diff line change
@@ -0,0 +1,64 @@
# typed: true
# frozen_string_literal: true

require "spec_helper"
require "ruby_lsp/tapioca/run_gem_rbi_check"

module Tapioca
module RubyLsp
class RunGemRbiCheckSpec < SpecWithProject
FOO_RB = <<~RUBY
module Foo
end
RUBY

# TODO: understand why this fails with `before(:all)`
# before(:all) do
before do
@project.tapioca("configure")
end

after do
project.write_gemfile!(project.tapioca_gemfile)
@project.require_default_gems
project.remove!("sorbet/rbi")
project.remove!("../gems")
project.remove!(".git")
project.remove!("sorbet/tapioca/require.rb")
project.remove!("config/application.rb")
ensure
@project.remove!("output")
end

it "does nothing if there is no git repo" do
foo = mock_gem("foo", "0.0.1") do
write!("lib/foo.rb", FOO_RB)
end
@project.require_mock_gem(foo)

@project.bundle_install!
check = ::RubyLsp::Tapioca::RunGemRbiCheck.new
check.run(@project.absolute_path)

assert check.logs.include?("Not a git repository")
end

it "create the RBI for a newly added gem" do
@project.exec("git init")
@project.exec("touch Gemfile.lock")
@project.exec("git add . && git commit -m 'Initial commit'")

foo = mock_gem("foo", "0.0.1") do
write!("lib/foo.rb", FOO_RB)
end
@project.require_mock_gem(foo)
@project.bundle_install!

check = ::RubyLsp::Tapioca::RunGemRbiCheck.new
check.run(@project.absolute_path)

assert_project_file_exist("sorbet/rbi/gems/[email protected]")
end
end
end
end

0 comments on commit cb3342c

Please sign in to comment.