lib/tapioca/cli.rb in tapioca-0.8.3 vs lib/tapioca/cli.rb in tapioca-0.9.0

- old
+ new

@@ -3,12 +3,11 @@ module Tapioca class Cli < Thor include CliHelper include ConfigHelper - include SorbetHelper - include ShimsHelper + include EnvHelper FILE_HEADER_OPTION_DESC = "Add a \"This file is generated\" header on top of each generated RBI file" class_option :config, aliases: ["-c"], @@ -20,16 +19,27 @@ aliases: ["-V"], type: :boolean, desc: "Verbose output for debugging purposes", default: false - desc "init", "initializes folder structure" + desc "init", "get project ready for type checking" def init - command = Commands::Init.new( + invoke(:configure) + invoke(:annotations) + invoke(:gem) + invoke(:todo) + + print_init_next_steps + end + + desc "configure", "initialize folder structure and type checking configuration" + option :postrequire, type: :string, default: DEFAULT_POSTREQUIRE_FILE + def configure + command = Commands::Configure.new( sorbet_config: SORBET_CONFIG_FILE, - tapioca_config: TAPIOCA_CONFIG_FILE, - default_postrequire: DEFAULT_POSTREQUIRE_FILE + tapioca_config: options[:config], + default_postrequire: options[:postrequire] ) command.execute end desc "require", "generate the list of files to be required by tapioca" @@ -98,12 +108,19 @@ desc: "EXPERIMENTAL: Number of parallel workers to use when generating RBIs", default: 1 option :rbi_max_line_length, type: :numeric, desc: "Set the max line length of generated RBIs. Signatures longer than the max line length will be wrapped", - default: 120 + default: DEFAULT_RBI_MAX_LINE_LENGTH + option :environment, + aliases: ["-e"], + type: :string, + desc: "The Rack/Rails environment to use when generating RBIs", + default: DEFAULT_ENVIRONMENT def dsl(*constants) + set_environment(options) + command = Commands::Dsl.new( requested_constants: constants, outpath: Pathname.new(options[:outdir]), only: options[:only], exclude: options[:exclude], @@ -192,13 +209,20 @@ desc: "The DSL directory used to correct gems strictnesses", default: DEFAULT_DSL_DIR option :rbi_max_line_length, type: :numeric, desc: "Set the max line length of generated RBIs. Signatures longer than the max line length will be wrapped", - default: 120 + default: DEFAULT_RBI_MAX_LINE_LENGTH + option :environment, + aliases: ["-e"], + type: :string, + desc: "The Rack/Rails environment to use when generating RBIs", + default: DEFAULT_ENVIRONMENT def gem(*gems) Tapioca.silence_warnings do + set_environment(options) + all = options[:all] verify = options[:verify] command = Commands::Gem.new( gem_names: all ? [] : gems, @@ -206,11 +230,11 @@ prerequire: options[:prerequire], postrequire: options[:postrequire], typed_overrides: options[:typed_overrides], outpath: Pathname.new(options[:outdir]), file_header: options[:file_header], - doc: options[:doc], + include_doc: options[:doc], include_exported_rbis: options[:exported_gem_rbis], number_of_workers: options[:workers], auto_strictness: options[:auto_strictness], dsl_dir: options[:dsl_dir], rbi_formatter: rbi_formatter(options) @@ -241,72 +265,49 @@ desc "check-shims", "check duplicated definitions in shim RBIs" option :gem_rbi_dir, type: :string, desc: "Path to gem RBIs", default: DEFAULT_GEM_DIR option :dsl_rbi_dir, type: :string, desc: "Path to DSL RBIs", default: DEFAULT_DSL_DIR option :shim_rbi_dir, type: :string, desc: "Path to shim RBIs", default: DEFAULT_SHIM_DIR + option :annotations_rbi_dir, type: :string, desc: "Path to annotations RBIs", default: DEFAULT_ANNOTATIONS_DIR + option :todo_rbi_file, type: :string, desc: "Path to the generated todo RBI file", default: DEFAULT_TODO_FILE option :payload, type: :boolean, desc: "Check shims against Sorbet's payload", default: true def check_shims - index = RBI::Index.new + command = Commands::CheckShims.new( + gem_rbi_dir: options[:gem_rbi_dir], + dsl_rbi_dir: options[:dsl_rbi_dir], + shim_rbi_dir: options[:shim_rbi_dir], + annotations_rbi_dir: options[:annotations_rbi_dir], + todo_rbi_file: options[:todo_rbi_file], + payload: options[:payload] + ) + command.execute + end - shim_rbi_dir = options[:shim_rbi_dir] - if !Dir.exist?(shim_rbi_dir) || Dir.empty?(shim_rbi_dir) - say("No shim RBIs to check", :green) - exit(0) - end - - payload_path = T.let(nil, T.nilable(String)) - - if options[:payload] - if sorbet_supports?(:print_payload_sources) - Dir.mktmpdir do |dir| - payload_path = dir - result = sorbet("--no-config --print=payload-sources:#{payload_path}") - - unless result.status - say_error("Sorbet failed to dump payload") - say_error(result.err) - exit(1) - end - - index_payload(index, payload_path) - end - else - say_error("The version of Sorbet used in your Gemfile.lock does not support `--print=payload-sources`") - say_error("Current: v#{SORBET_GEM_SPEC.version}") - say_error("Required: #{FEATURE_REQUIREMENTS[:print_payload_sources]}") - exit(1) - end - end - - index_rbis(index, "shim", shim_rbi_dir) - index_rbis(index, "gem", options[:gem_rbi_dir]) - index_rbis(index, "dsl", options[:dsl_rbi_dir]) - - duplicates = duplicated_nodes_from_index(index, shim_rbi_dir) - unless duplicates.empty? - duplicates.each do |key, nodes| - say_error("\nDuplicated RBI for #{key}:", :red) - nodes.each do |node| - node_loc = node.loc - next unless node_loc - - loc_string = location_to_payload_url(node_loc, path_prefix: payload_path) - say_error(" * #{loc_string}", :red) - end - end - say_error("\nPlease remove the duplicated definitions from the #{shim_rbi_dir} directory.", :red) + desc "annotations", "Pull gem RBI annotations from remote sources" + option :sources, type: :array, default: [CENTRAL_REPO_ROOT_URI], + desc: "URIs of the sources to pull gem RBI annotations from" + option :netrc, type: :boolean, default: true, desc: "Use .netrc to authenticate to private sources" + option :netrc_file, type: :string, desc: "Path to .netrc file" + option :auth, type: :string, default: nil, desc: "HTTP authorization header for private sources" + option :typed_overrides, + aliases: ["--typed", "-t"], + type: :hash, + banner: "gem:level [gem:level ...]", + desc: "Override for typed sigils for pulled annotations", + default: {} + def annotations + if !options[:netrc] && options[:netrc_file] + say_error("Options `--no-netrc` and `--netrc-file` can't be used together", :bold, :red) exit(1) end - say("\nNo duplicates found in shim RBIs", :green) - exit(0) - end - - desc "annotations", "Pull gem annotations from a central RBI repository" - option :repo_uri, type: :string, desc: "Repository URI to pull annotations from", default: CENTRAL_REPO_ROOT_URI - def annotations - command = Commands::Annotations.new(central_repo_root_uri: options[:repo_uri]) + command = Commands::Annotations.new( + central_repo_root_uris: options[:sources], + auth: options[:auth], + netrc_file: netrc_file(options), + typed_overrides: options[:typed_overrides] + ) command.execute end map ["--version", "-v"] => :__print_version @@ -317,8 +318,64 @@ no_commands do def self.exit_on_failure? true end + end + + private + + def print_init_next_steps + say(<<~OUTPUT) + #{set_color("This project is now set up for use with Sorbet and Tapioca", :bold)} + + The sorbet/ folder should exist and look something like this: + + ├── config # Default options to be passed to Sorbet on every run + └── rbi/ + ├── annotations/ # Type definitions pulled from the rbi-central repository + ├── gems/ # Autogenerated type definitions for your gems + └── todo.rbi # Constants which were still missing after RBI generation + └── tapioca/ + ├── config.yml # Default options to be passed to Tapioca + └── require.rb # A file where you can make requires from gems that might be needed for gem RBI generation + + Please check this folder into version control. + + #{set_color("🤔 What's next", :bold)} + + 1. Many Ruby applications use metaprogramming DSLs to dynamically generate constants and methods. + To generate type definitions for any DSLs in your application, run: + + #{set_color("bin/tapioca dsl", :cyan)} + + 2. Check whether the constants in the #{set_color("sorbet/rbi/todo.rbi", :cyan)} file actually exist in your project. + It is possible that some of these constants are typos, and leaving them in #{set_color("todo.rbi", :cyan)} will + hide errors in your application. Ideally, you should be able to remove all definitions + from this file and delete it. + + 3. Typecheck your project: + + #{set_color("bundle exec srb tc", :cyan)} + + There should not be any typechecking errors. + + 4. Upgrade a file marked "#{set_color("# typed: false", :cyan)}" to "#{set_color("# typed: true", :cyan)}". + Then, run: #{set_color("bundle exec srb tc", :cyan)} and try to fix any errors. + + You can use Spoom to bump files for you: + + #{set_color("spoom bump --from false --to true", :cyan)} + + To learn more about Spoom, visit: #{set_color("https://github.com/Shopify/spoom", :cyan)} + + 5. Add signatures to your methods with #{set_color("sig", :cyan)}. To learn how, read: #{set_color("https://sorbet.org/docs/sigs", :cyan)} + + #{set_color("Documentation", :bold)} + We recommend skimming these docs to get a feel for how to use Sorbet: + - Gradual Type Checking: #{set_color("https://sorbet.org/docs/gradual", :cyan)} + - Enabling Static Checks: #{set_color("https://sorbet.org/docs/static", :cyan)} + - RBI Files: #{set_color("https://sorbet.org/docs/rbi", :cyan)} + OUTPUT end end end