diff --git a/README.md b/README.md index 3b6ee55d..917fd144 100644 --- a/README.md +++ b/README.md @@ -125,7 +125,7 @@ in order to avoid the rate limit of the providers. X = N / {PRONTO_WARNINGS_PER_REVIEW || warnings_per_review || 30}) ``` -Note: In case no environment variable or config setting is specified in `.pronto.yml`, +Note: In case no environment variable or config setting is specified in `.pronto.yml`, a default value of `30` will be used. ```sh @@ -164,7 +164,7 @@ Pronto.run('origin/master', '.', formatters) #### GitHub Actions Integration -You can also run Pronto as a GitHub action. +You can also run Pronto as a GitHub action. Here's an example `.github/workflows/pronto.yml` workflow file using the `github_status` and `github_pr` formatters and running on each GitHub PR, with `pronto-rubocop` as the runner: @@ -290,6 +290,8 @@ bitbucket: max_warnings: 150 warnings_per_review: 30 verbose: false +runners: [rubocop, eslint] # only listed runners will be executed +skip_runners: [reek] # all, except listed runners will be executed ``` All properties that can be specified via `.pronto.yml`, can also be specified @@ -297,6 +299,14 @@ via environment variables. Their names will be the upcased path to the property. For example: `PRONTO_GITHUB_SLUG` or `PRONTO_GITLAB_API_PRIVATE_TOKEN`. Environment variables will always take precedence over values in configuration file. +| Property | Description | +|-----------------------|--------------------------------------------------------------------------------------| +| `max_warnings` | Limits the amount of warnings. Returns all warnings if option is skipped. | +| `runners` | Runs only listed runners. Runs everything if option is skipped. | +| `skip_runners` | All, except listed runners will be executed. Runs everything if option is skipped. | +| `verbose` | Outputs more information when set to `true`. | +| `warnings_per_review` | Limits the amount of warnings per review. Returns all warnings if option is skipped. | + ### Message format Pronto allows you to configure the format of the messages that are produced. You diff --git a/lib/pronto/config.rb b/lib/pronto/config.rb index 59c9dc6d..f01e9c43 100644 --- a/lib/pronto/config.rb +++ b/lib/pronto/config.rb @@ -59,11 +59,11 @@ def bitbucket_hostname end def warnings_per_review - ENV['PRONTO_WARNINGS_PER_REVIEW'] && Integer(ENV['PRONTO_WARNINGS_PER_REVIEW']) || @config_hash['warnings_per_review'] + fetch_integer('warnings_per_review') end def max_warnings - ENV['PRONTO_MAX_WARNINGS'] && Integer(ENV['PRONTO_MAX_WARNINGS']) || @config_hash['max_warnings'] + fetch_integer('max_warnings') end def message_format(formatter) @@ -71,15 +71,45 @@ def message_format(formatter) if formatter_config && formatter_config.key?('format') formatter_config['format'] else - ENV['PRONTO_FORMAT'] || @config_hash['format'] + fetch_value('format') end end + def skip_runners + fetch_list('skip_runners') + end + + def runners + fetch_list('runners') + end + def logger @logger ||= begin - verbose = ENV['PRONTO_VERBOSE'] || @config_hash['verbose'] + verbose = fetch_value('verbose') verbose ? Logger.new($stdout) : Logger.silent end end + + private + + def fetch_integer(key) + full_key = env_key(key) + + (ENV[full_key] && Integer(ENV[full_key])) || @config_hash[key] + end + + def fetch_value(key) + ENV[env_key(key)] || @config_hash[key] + end + + def env_key(key) + "PRONTO_#{key.upcase}" + end + + def fetch_list(key) + Array(fetch_value(key)).flat_map do |runners| + runners.split(',') + end + end end end diff --git a/lib/pronto/runners.rb b/lib/pronto/runners.rb index 920a54d2..be27480e 100644 --- a/lib/pronto/runners.rb +++ b/lib/pronto/runners.rb @@ -6,25 +6,42 @@ def initialize(runners = Runner.runners, config = Config.new) end def run(patches) - patches = reject_excluded(@config.excluded_files('all'), patches) + patches = reject_excluded(config.excluded_files('all'), patches) return [] if patches.none? result = [] - @runners.each do |runner| + active_runners.each do |runner| next if exceeds_max?(result) - @config.logger.log("Running #{runner}") + config.logger.log("Running #{runner}") runner_patches = reject_excluded( - @config.excluded_files(runner.title), patches + config.excluded_files(runner.title), patches ) next if runner_patches.none? result += runner.new(runner_patches, patches.commit).run.flatten.compact end - result = result.take(@config.max_warnings) if @config.max_warnings + result = result.take(config.max_warnings) if config.max_warnings result end private + attr_reader :config, :runners + + def active_runners + runners.select { |runner| active_runner?(runner) } + end + + def active_runner?(runner) + return true if config.runners.empty? && config.skip_runners.empty? + + if config.runners.empty? + !config.skip_runners.include?(runner.title) + else + active_runner_names = config.runners - config.skip_runners + active_runner_names.include?(runner.title) + end + end + def reject_excluded(excluded_files, patches) return patches unless excluded_files.any? @@ -34,7 +51,7 @@ def reject_excluded(excluded_files, patches) end def exceeds_max?(warnings) - @config.max_warnings && warnings.count >= @config.max_warnings + config.max_warnings && warnings.count >= config.max_warnings end end end diff --git a/spec/pronto/config_spec.rb b/spec/pronto/config_spec.rb index 620b3336..ceaae0f0 100644 --- a/spec/pronto/config_spec.rb +++ b/spec/pronto/config_spec.rb @@ -147,5 +147,52 @@ module Pronto it { should == ConfigFile::DEFAULT_MESSAGE_FORMAT } end end + + describe '#skip_runners' do + subject { config.skip_runners } + + let(:env_variables) { {} } + + before do + stub_const('ENV', env_variables) + end + + context 'when runners are not skipped' do + it { should be_empty } + end + + context 'when runners are skipped via ENV variable' do + let(:env_variables) { { 'PRONTO_SKIP_RUNNERS' => 'Runner,OtherRunner' } } + + it { should == %w[Runner OtherRunner] } + end + + context 'when runners are skipped via config file' do + let(:config_hash) { { 'skip_runners' => ['Runner'] } } + + it { should == %w[Runner] } + end + + context 'when runners are skipped via config file and ENV variable' do + let(:env_variables) { { 'PRONTO_SKIP_RUNNERS' => 'EnvRunner' } } + let(:config_hash) { { 'skip_runners' => %w[ConfigRunner] } } + + it { should == %w[EnvRunner] } + end + end + + describe '#runners' do + subject { config.runners } + + context 'when there is an entry in the config file' do + let(:config_hash) { { 'runners' => ['Runner'] } } + + it { should == %w[Runner] } + end + + context 'when there is no entry in the config file' do + it { should be_empty } + end + end end end diff --git a/spec/pronto/runners_spec.rb b/spec/pronto/runners_spec.rb index a6e1edd0..b2423ec8 100644 --- a/spec/pronto/runners_spec.rb +++ b/spec/pronto/runners_spec.rb @@ -5,13 +5,8 @@ module Pronto let(:patches) { double(commit: nil, none?: false) } let(:config) { Config.new } - context 'no runners' do - let(:runners) { [] } - it { should be_empty } - end - - context 'fake runner' do - class FakeRunner + let(:fake_runner) do + Class.new do def self.title 'fake_runner' end @@ -22,8 +17,15 @@ def run [1, nil, [3]] end end + end + + context 'no runners' do + let(:runners) { [] } + it { should be_empty } + end - let(:runners) { [FakeRunner] } + context 'fake runner' do + let(:runners) { [fake_runner] } it { should == [1, 3] } context 'rejects excluded files' do @@ -38,6 +40,59 @@ def run it { should == [1] } end end + + context 'when multiple runners exist' do # rubocop:disable Metrics/BlockLength + let(:fake_runner_2) do + Class.new(fake_runner) do + def self.title + 'fake_runner_2' + end + + def run + [2, nil, [6]] + end + end + end + + let(:fake_runner_3) do + Class.new(fake_runner) do + def self.title + 'fake_runner_2' + end + + def run + [5, nil, [10]] + end + end + end + + let(:runners) { [fake_runner, fake_runner_2, fake_runner_3] } + + context 'when runners are not filtered' do + it { should == [1, 3, 2, 6, 5, 10] } + end + + context 'when runners are listed in config' do + before { config.stub(:runners) { ['fake_runner'] } } + + it { should == [1, 3] } + end + + context 'when some runners are skipped via config' do + before { config.stub(:skip_runners) { ['fake_runner'] } } + + it { should == [2, 6, 5, 10] } + end + + context 'when same runners are skipped and some are listed' do + before do + config.stub(:runners) { %w[fake_runner fake_runner_3] } + config.stub(:skip_runners) { %w[fake_runner_3] } + end + + it { should == [1, 3] } + end + end end end end