Skip to content

Commit

Permalink
[Fix rubocop#9580] Add New Cop to Enforce Bundler Gem filename
Browse files Browse the repository at this point in the history
Add a new cop which enforces which bundler gem filename to use. By default, enforces Gemfile and its related Gemfile.lock file. Alternatively, setting RequiresGemfile to false enforces gems.rb and its related gems.locked file.
  • Loading branch information
gregfletch committed Aug 22, 2021
1 parent ed7aa5c commit 96cb9c5
Show file tree
Hide file tree
Showing 5 changed files with 183 additions and 0 deletions.
1 change: 1 addition & 0 deletions changelog/fix_enforce_gem_file_name.md
@@ -0,0 +1 @@
* [#9580](https://github.com/rubocop/rubocop/issues/9580): Add a new cop that enforces which bundler gem file to use. ([@gregfletch][])
6 changes: 6 additions & 0 deletions config/default.yml
Expand Up @@ -174,6 +174,12 @@ Bundler/GemComment:
IgnoredGems: []
OnlyFor: []

Bundler/GemFilename:
Description: 'Enforces the filename for managing gems.'
Enabled: true
VersionAdded: '<<next>>'
RequiresGemfile: true

Bundler/GemVersion:
Description: 'Requires or forbids specifying gem versions.'
Enabled: false
Expand Down
1 change: 1 addition & 0 deletions lib/rubocop.rb
Expand Up @@ -152,6 +152,7 @@

require_relative 'rubocop/cop/bundler/duplicated_gem'
require_relative 'rubocop/cop/bundler/gem_comment'
require_relative 'rubocop/cop/bundler/gem_filename'
require_relative 'rubocop/cop/bundler/gem_version'
require_relative 'rubocop/cop/bundler/insecure_protocol_source'
require_relative 'rubocop/cop/bundler/ordered_gems'
Expand Down
90 changes: 90 additions & 0 deletions lib/rubocop/cop/bundler/gem_filename.rb
@@ -0,0 +1,90 @@
# frozen_string_literal: true

module RuboCop
module Cop
module Bundler
# This cop verifies that a project contains Gemfile or gems.rb file and correct
# associated lock file based on the configuration.
#
# @example RequiresGemfile: true (default)
# # bad
# Project contains gems.rb and gems.locked files
#
# # bad
# Project contains Gemfile and gems.locked file
#
# # good
# Project contains Gemfile and Gemfile.lock
#
# @example RequiresGemfile: false
# # bad
# Project contains Gemfile and Gemfile.lock files
#
# # bad
# Project contains gems.rb and Gemfile.lock file
#
# # good
# Project contains gems.rb and gems.locked files
class GemFilename < Base
include RangeHelp

MSG_GEMFILE_REQUIRED = 'gems.rb file was found but Gemfile is required.'
MSG_GEMS_RB_REQUIRED = 'Gemfile was found but gems.rb file is required.'
MSG_GEMFILE_MISMATCHED = 'Expected a Gemfile.lock with Gemfile but found gems.locked file.'
MSG_GEMS_RB_MISMATCHED = 'Expected a gems.locked file with gems.rb but found Gemfile.lock.'
GEMFILE_REQUIRED_CONFIG = 'RequiresGemfile'
GEMFILE_FILES = %w[Gemfile Gemfile.lock].freeze
GEMS_RB_FILES = %w[gems.rb gems.locked].freeze

def on_new_investigation
file_path = processed_source.file_path
return if expected_gemfile?(file_path)

register_offense(processed_source, file_path)
end

private

def register_offense(processed_source, file_path)
register_gemfile_offense(processed_source, file_path) if gemfile_required?
register_gems_rb_offense(processed_source, file_path) unless gemfile_required?
end

def register_gemfile_offense(processed_source, file_path)
message = case file_path
when 'gems.rb'
MSG_GEMFILE_REQUIRED
when 'gems.locked'
MSG_GEMFILE_MISMATCHED
end

return if message.nil?

add_offense(source_range(processed_source.buffer, 1, 0), message: message)
end

def register_gems_rb_offense(processed_source, file_path)
message = case file_path
when 'Gemfile'
MSG_GEMS_RB_REQUIRED
when 'Gemfile.lock'
MSG_GEMS_RB_MISMATCHED
end

return if message.nil?

add_offense(source_range(processed_source.buffer, 1, 0), message: message)
end

def expected_gemfile?(file_path)
(gemfile_required? && GEMFILE_FILES.include?(file_path)) ||
(!gemfile_required? && GEMS_RB_FILES.include?(file_path))
end

def gemfile_required?
cop_config[GEMFILE_REQUIRED_CONFIG]
end
end
end
end
end
85 changes: 85 additions & 0 deletions spec/rubocop/cop/bundler/gem_filename_spec.rb
@@ -0,0 +1,85 @@
# frozen_string_literal: true

RSpec.describe RuboCop::Cop::Bundler::GemFilename, :config do
shared_examples_for 'invalid gem file' do |message|
it 'registers an offense' do
offenses = _investigate(cop, processed_source)

expect(offenses.size).to eq(1)
expect(offenses.first.message).to eq(message)
end
end

shared_examples_for 'valid gem file' do
it 'does not register an offense' do
offenses = _investigate(cop, processed_source)

expect(offenses.size).to eq(0)
end
end

context 'with default configuration' do
let(:source) { 'print 1' }
let(:processed_source) { parse_source(source) }

before { allow(processed_source.buffer).to receive(:name).and_return(filename) }

context 'with gems.rb file path' do
let(:filename) { 'gems.rb' }

include_examples 'invalid gem file', 'gems.rb file was found but Gemfile is required.'
end

context 'with gems.locked file path' do
let(:filename) { 'gems.locked' }

include_examples 'invalid gem file',
'Expected a Gemfile.lock with Gemfile but found gems.locked file.'
end

context 'with Gemfile file path' do
let(:filename) { 'Gemfile' }

include_examples 'valid gem file'
end

context 'with Gemfile.lock file path' do
let(:filename) { 'Gemfile.lock' }

include_examples 'valid gem file'
end
end

context 'with RequiresGemfile set to false' do
let(:source) { 'print 1' }
let(:processed_source) { parse_source(source) }
let(:cop_config) { { 'RequiresGemfile' => false } }

before { allow(processed_source.buffer).to receive(:name).and_return(filename) }

context 'with Gemfile file path' do
let(:filename) { 'Gemfile' }

include_examples 'invalid gem file', 'Gemfile was found but gems.rb file is required.'
end

context 'with Gemfile.lock file path' do
let(:filename) { 'Gemfile.lock' }

include_examples 'invalid gem file',
'Expected a gems.locked file with gems.rb but found Gemfile.lock.'
end

context 'with gems.rb file path' do
let(:filename) { 'gems.rb' }

include_examples 'valid gem file'
end

context 'with gems.locked file path' do
let(:filename) { 'gems.locked' }

include_examples 'valid gem file'
end
end
end

0 comments on commit 96cb9c5

Please sign in to comment.