diff --git a/lib/steep/rake_task.rb b/lib/steep/rake_task.rb new file mode 100644 index 000000000..5e8e15a92 --- /dev/null +++ b/lib/steep/rake_task.rb @@ -0,0 +1,128 @@ +require "rake" +require "rake/tasklib" + +module Steep + # Provides Rake tasks for running Steep commands. + # + # require "steep/rake_task" + # Steep::RakeTask.new do |t| + # t.check.severity_level = :error + # t.watch.verbose + # end + class RakeTask < Rake::TaskLib + attr_accessor :name + + def self.available_commands + require "steep/cli" + + skipped_commands = %i[langserver checkfile] + + Steep::CLI.available_commands - skipped_commands + end + + def initialize(name = :steep, cli_runner = default_cli_runner) + super() + + @name = name + + configuration = TaskConfiguration.new + + yield configuration if block_given? + + define_tasks(configuration, cli_runner) + end + + private + + # :nodoc: + class TaskConfiguration + def initialize + @commands = {} + end + + def method_missing(command) + if respond_to?(command) + @commands[command] ||= CommandConfiguration.new + else + super + end + end + + def respond_to_missing?(name, include_private = false) + RakeTask.available_commands.include?(name) || super + end + + def options(command) + @commands[command]&.to_a || [] + end + end + + # :nodoc: + class CommandConfiguration + def initialize + @options = [] + end + + def method_missing(name, value = nil) + @options << "--#{name.to_s.gsub(/_/, '-').gsub(/=/, '')}" + @options << value.to_s unless value.nil? + end + + def respond_to_missing?(_name) + true + end + + def to_a + @options + end + end + + def default_cli_runner + lambda do |arguments| + require "steep" + + cli = Steep::CLI.new( + stdout: $stdout, + stdin: $stdin, + stderr: $stderr, + argv: arguments + ) + + cli.run + end + end + + def define_tasks(configuration, cli_runner) + namespace name do + RakeTask.available_commands.each do |command| + desc "Run steep #{command}" + + task command do |_, args| + configured_options = configuration.options(command) + + argv = [ + command.to_s, + *configured_options, + *args.extras + ] + + result = cli_runner[argv] + + raise "Steep failed" if result.nonzero? + end + end + + desc "Run steep help" + task "help" do + arguments = ["--help"] + + cli_runner[arguments] + end + end + + # Default steep task to steep:check + desc "Run steep check" unless ::Rake.application.last_description + task name => ["#{name}:check"] + end + end +end diff --git a/test/rake_task_test.rb b/test/rake_task_test.rb new file mode 100644 index 000000000..bc0aa2839 --- /dev/null +++ b/test/rake_task_test.rb @@ -0,0 +1,86 @@ +require_relative "test_helper" + +require_relative "../lib/steep/rake_task" + +require "minitest/mock" + +class RakeTaskTest < Minitest::Test + def test_task_configuration + configuration = Steep::RakeTask::TaskConfiguration.new + + configuration.check.severity_level = :error + configuration.watch.verbose + + assert_raises(NoMethodError) do + configuration.missing_command.verbose + end + + assert_equal ["--severity-level", "error"], configuration.options(:check) + assert_equal ["--verbose"], configuration.options(:watch) + assert_equal [], configuration.options(:stats) + end + + def test_define_task_with_options + cli = mock_cli(expecting: %w[check --severity-level error]) + + setup_rake_tasks!(cli) do |task| + task.check.severity_level = :error + end + + Rake::Task["steep:check"].invoke + end + + def test_rake_arguments + cli = mock_cli(expecting: %w[check --severity-level error]) + + setup_rake_tasks!(cli) + + Rake::Task["steep:check"].invoke("--severity-level", "error") + end + + def test_help_task + cli = mock_cli(expecting: %w[--help]) + + setup_rake_tasks!(cli) + + Rake::Task["steep:help"].invoke + end + + def test_default_task + cli = mock_cli(expecting: %w[check --verbose]) + + setup_rake_tasks!(cli) do |task| + task.check.verbose + end + + Rake::Task["steep"].invoke + end + + def test_skipped_commands + setup_rake_tasks! + + assert Rake::Task.task_defined?("steep:help") + + refute Rake::Task.task_defined?("steep:langserver") + end + + private + + def setup_rake_tasks!(cli_runner = nil, &block) + Rake::Task.clear + + Steep::RakeTask.new(:steep, cli_runner, &block) + end + + def mock_cli(expecting:) + cli = Minitest::Mock.new + + cli.expect(:run, nil, [expecting]) + + lambda do |arguments| + cli.run(arguments) + + 0 + end + end +end