Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
35 changes: 23 additions & 12 deletions lib/spoom/cli/srb/bump.rb
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,8 @@

require "find"
require "open3"
require "securerandom"
require "yaml"

module Spoom
module Cli
Expand Down Expand Up @@ -72,6 +74,11 @@ def bump(directory = ".")
exit(1)
end

unless context.sorbet_config.typed_overrides.empty?
say_error("Cannot run `spoom bump` on a project that already uses Sorbet's `--typed-override` option")
exit(1)
end

say("Checking files...")

files_to_bump = context.srb_files_with_strictness(from, include_rbis: false)
Expand All @@ -90,18 +97,18 @@ def bump(directory = ".")
exit(0)
end

Sorbet::Sigils.change_sigil_in_files(files_to_bump, to)

if force
Sorbet::Sigils.change_sigil_in_files(files_to_bump, to) unless dry
print_changes(files_to_bump, command: cmd, from: from, to: to, dry: dry, path: exec_path)
undo_changes(files_to_bump, from) if dry
exit(files_to_bump.empty?)
end

error_url_base = Spoom::Sorbet::Errors::DEFAULT_ERROR_URL_BASE
typed_override_file = write_typed_override_file(context, files_to_bump, to)
result = begin
T.unsafe(context).srb_tc(
*options[:sorbet_options].split(" "),
"--typed-override=#{typed_override_file}",
"--error-url-base=#{error_url_base}",
capture_err: true,
sorbet_bin: options[:sorbet],
Expand All @@ -114,7 +121,6 @@ def bump(directory = ".")
It means one of the file bumped to `typed: #{to}` made Sorbet crash.
Run `spoom bump -f` locally followed by `bundle exec srb tc` to investigate the problem.
ERR
undo_changes(files_to_bump, from)
exit(error.result.exit_code)
rescue Spoom::Sorbet::Error::Killed => error
say_error(<<~ERR, status: nil)
Expand All @@ -123,21 +129,21 @@ def bump(directory = ".")
It means Sorbet was killed while executing. Changes to files have not been applied.
Re-run `spoom bump` to try again.
ERR
undo_changes(files_to_bump, from)
exit(error.result.exit_code)
ensure
context.remove!(typed_override_file)
end

if result.status
Sorbet::Sigils.change_sigil_in_files(files_to_bump, to) unless dry
print_changes(files_to_bump, command: cmd, from: from, to: to, dry: dry, path: exec_path)
undo_changes(files_to_bump, from) if dry
exit(files_to_bump.empty?)
end

unless result.exit_code == 100
# Sorbet will return exit code 100 if there are type checking errors.
# If Sorbet returned something else, it means it didn't terminate normally.
say_error(result.err, status: nil, nl: false)
undo_changes(files_to_bump, from)
exit(1)
end

Expand All @@ -156,13 +162,11 @@ def bump(directory = ".")
path
end.compact.uniq

undo_changes(files_with_errors, from)

say("Found #{errors.length} type checking error#{"s" if errors.length > 1}") if options[:count_errors]

files_changed = files_to_bump - files_with_errors
Sorbet::Sigils.change_sigil_in_files(files_changed, to) unless dry
print_changes(files_changed, command: cmd, from: from, to: to, dry: dry, path: exec_path)
undo_changes(files_to_bump, from) if dry
exit(files_changed.empty?)
end

Expand All @@ -189,8 +193,15 @@ def print_changes(files, command:, from: "false", to: "true", dry: false, path:
end
end

def undo_changes(files, from_strictness)
Sorbet::Sigils.change_sigil_in_files(files, from_strictness)
#: (Spoom::Context context, Array[String] files, String strictness) -> String
def write_typed_override_file(context, files, strictness)
path = ".spoom-typed-override-#{Process.pid}-#{SecureRandom.hex(8)}.yaml"

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.

Can't we just use Tempfile.new(['spoom-typed-override', '.yml'])?

Then there's no need to have it hidden with the prefixed .

relative_paths = files.map do |file|
relative_path = file.delete_prefix("#{context.absolute_path}/")
"./#{relative_path}"
end
context.write!(path, YAML.dump({ strictness => relative_paths }))

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

Do we delete the file when we're done?
Is that was using context helps with?

path
end
end
end
Expand Down
13 changes: 12 additions & 1 deletion lib/spoom/sorbet/config.rb
Original file line number Diff line number Diff line change
Expand Up @@ -27,7 +27,7 @@ class Config
DEFAULT_ALLOWED_EXTENSIONS = [".rb", ".rbi"].freeze #: Array[String]

#: Array[String]
attr_accessor :paths, :ignore, :allowed_extensions
attr_accessor :paths, :ignore, :allowed_extensions, :typed_overrides

#: bool
attr_accessor :no_stdlib
Expand All @@ -37,6 +37,7 @@ def initialize
@paths = [] #: Array[String]
@ignore = [] #: Array[String]
@allowed_extensions = [] #: Array[String]
@typed_overrides = [] #: Array[String]
@no_stdlib = false #: bool
end

Expand All @@ -46,6 +47,7 @@ def initialize_copy(source)
@paths = @paths.dup
@ignore = @ignore.dup
@allowed_extensions = @allowed_extensions.dup
@typed_overrides = @typed_overrides.dup
end

# Returns self as a string of options that can be passed to Sorbet
Expand All @@ -66,6 +68,7 @@ def options_string
opts.concat(paths.map { |p| "'#{p}'" })
opts.concat(ignore.map { |p| "--ignore '#{p}'" })
opts.concat(allowed_extensions.map { |ext| "--allowed-extension '#{ext}'" })
opts.concat(typed_overrides.map { |path| "--typed-override '#{path}'" })
opts << "--no-stdlib" if @no_stdlib
opts.join(" ")
end
Expand Down Expand Up @@ -95,6 +98,12 @@ def parse_string(sorbet_config)
when /^--ignore=/
config.ignore << parse_option(line)
next
when /^--typed-override$/
state = :typed_override
next
when /^--typed-override=/
config.typed_overrides << parse_option(line)
next
when /^--file$/
next
when /^--file=/
Expand Down Expand Up @@ -124,6 +133,8 @@ def parse_string(sorbet_config)
config.ignore << line
when :extension
config.allowed_extensions << line
when :typed_override
config.typed_overrides << line
when :skip
# nothing
else
Expand Down
6 changes: 5 additions & 1 deletion rbi/spoom.rbi
Original file line number Diff line number Diff line change
Expand Up @@ -128,7 +128,9 @@ class Spoom::Cli::Srb::Bump < ::Thor

def help(command = T.unsafe(nil), subcommand = T.unsafe(nil)); end
def print_changes(files, command:, from: T.unsafe(nil), to: T.unsafe(nil), dry: T.unsafe(nil), path: T.unsafe(nil)); end
def undo_changes(files, from_strictness); end

sig { params(context: ::Spoom::Context, files: T::Array[::String], strictness: ::String).returns(::String) }
def write_typed_override_file(context, files, strictness); end
end

class Spoom::Cli::Srb::Coverage < ::Thor
Expand Down Expand Up @@ -2661,6 +2663,8 @@ class Spoom::Sorbet::Config
def paths; end

def paths=(_arg0); end
def typed_overrides; end
def typed_overrides=(_arg0); end

private

Expand Down
30 changes: 30 additions & 0 deletions test/spoom/cli/srb/bump_test.rb
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,12 @@ def setup
@project.bundle_install!
end

def teardown
assert_empty(Dir.glob("#{@project.absolute_path}/.spoom-typed-override-*"))
ensure
super
end

def test_bump_outside_sorbet_dir
@project.remove!("sorbet/config")
result = @project.spoom("srb bump --no-color")
Expand Down Expand Up @@ -54,6 +60,30 @@ class A; end
assert_equal("false", @project.read_file_strictness("file2.rb"))
end

def test_bump_errors_if_sorbet_config_has_typed_override
@project.write_sorbet_config!(<<~CONFIG)
.
--typed-override=typed-override.yaml
CONFIG
@project.write!("typed-override.yaml", <<~YAML)
true:
- ./file.rb
YAML
@project.write!("file.rb", <<~RB)
# typed: false
class A; end
RB

result = @project.spoom("srb bump --no-color")
assert_empty(result.out)
assert_equal(<<~ERR, result.err)
Error: Cannot run `spoom bump` on a project that already uses Sorbet's `--typed-override` option
ERR
refute(result.status)

assert_equal("false", @project.read_file_strictness("file.rb"))
end

def test_bump_doesnt_change_sigils_outside_directory
@project.write!("lib/a/file.rb", "# typed: false")
@project.write!("lib/b/file.rb", "# typed: false")
Expand Down
15 changes: 15 additions & 0 deletions test/spoom/sorbet/config_test.rb
Original file line number Diff line number Diff line change
Expand Up @@ -71,6 +71,19 @@ def test_parses_a_config_string_with_ignore_options
assert_empty(config.allowed_extensions)
end

def test_parses_a_config_string_with_typed_override_options
config = Spoom::Sorbet::Config.parse_string(<<~CONFIG)
a
--typed-override=b.yaml
c
--typed-override
d.yaml
e
CONFIG
assert_equal(["a", "c", "e"], config.paths)
assert_equal(["b.yaml", "d.yaml"], config.typed_overrides)
end

def test_parses_a_config_string_with_other_options
config = Spoom::Sorbet::Config.parse_string(<<~CONFIG)
a
Expand Down Expand Up @@ -241,12 +254,14 @@ def test_options_string_with_options
--ignore=.git/
--ignore=vendor/
--allowed-extension=.rb
--typed-override=typed-override.yaml
--no-stdlib
CONFIG
expected = "'.' " \
"--ignore '.git/' " \
"--ignore 'vendor/' " \
"--allowed-extension '.rb' " \
"--typed-override 'typed-override.yaml' " \
"--no-stdlib"
assert_equal(expected, config.options_string)
end
Expand Down
Loading