pax_global_header00006660000000000000000000000064137442767530014534gustar00rootroot0000000000000052 comment=ad1230361077d0c02bf62caa677938e5ed809ef4 rubocop-packaging-0.5.1/000077500000000000000000000000001374427675300151325ustar00rootroot00000000000000rubocop-packaging-0.5.1/.gitignore000066400000000000000000000002551374427675300171240ustar00rootroot00000000000000/.bundle/ /.yardoc /_yardoc/ /coverage/ /doc/ /pkg/ /spec/reports/ /tmp/ /vendor/ Gemfile.lock # rspec failure tracking .rspec_status # tracking remaining work TODO FIXME rubocop-packaging-0.5.1/.rspec000066400000000000000000000000651374427675300162500ustar00rootroot00000000000000--format documentation --color --require spec_helper rubocop-packaging-0.5.1/.rubocop.yml000066400000000000000000000010521374427675300174020ustar00rootroot00000000000000require: - rubocop-packaging AllCops: NewCops: enable Exclude: - '.bundle/**/*' - 'spec/fixtures/**/*' - 'tmp/**/*' - 'vendor/**/*' TargetRubyVersion: 2.4 Naming/FileName: Exclude: - lib/rubocop-packaging.rb Metrics/BlockLength: Exclude: - tasks/*.rake ExcludedMethods: - configure - describe - context - shared_examples Metrics/MethodLength: Exclude: - tasks/*.rake Style/StringLiterals: EnforcedStyle: double_quotes Style/StringLiteralsInInterpolation: EnforcedStyle: double_quotes rubocop-packaging-0.5.1/.travis.yml000066400000000000000000000002361374427675300172440ustar00rootroot00000000000000--- language: ruby cache: bundler before_install: gem install bundler -v 2.1.4 rvm: - 2.5.8 - 2.6.6 - 2.7.2 - ruby-head allow_failures: - ruby-head rubocop-packaging-0.5.1/Gemfile000066400000000000000000000002051374427675300164220ustar00rootroot00000000000000# frozen_string_literal: true source "https://rubygems.org" # Specify your gem's dependencies in rubocop-packaging.gemspec gemspec rubocop-packaging-0.5.1/LICENSE000066400000000000000000000021221374427675300161340ustar00rootroot00000000000000MIT License =========== Copyright (©) 2020 Utkarsh Gupta Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions: The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. rubocop-packaging-0.5.1/README.md000066400000000000000000000043361374427675300164170ustar00rootroot00000000000000# RuboCop::Packaging `RuboCop::Packaging` is an extension of [RuboCop](https://rubocop.org/), which is a Ruby static code analyzer (a.k.a. linter) and code formatter. It helps enforcing some of the guidelines that are expected of upstream maintainers so that the downstream can build their packages in a clean environment without any problems. ## Documentation A detailed documentation, explaining what this extension is doing and the reasoning behind it, can be found here: https://docs.rubocop.org/rubocop-packaging/ We also have a [packaging-style-guide](https://packaging.rubystyle.guide/), listing some good and bad examples and the rationale behind these cops. In case anything is not clear, please feel free to raise an issue, asking for more explanation! ## Installation Add this line to your application's Gemfile: ```ruby gem 'rubocop-packaging' ``` And then execute: ```bash $ bundle install ``` Or install it yourself as: ```bash $ gem install rubocop-packaging ``` ## Usage You need to tell RuboCop to load the Packaging extension. There are three ways to do this: ### RuboCop configuration file Put this into your `.rubocop.yml` file: ```yaml require: rubocop-packaging ``` Alternatively, use the following array notation when specifying multiple extensions: ```yaml require: - rubocop-other-extension - rubocop-packaging ``` Now you can run `rubocop` and it will automatically load the RuboCop Packaging cops together with the standard cops. ### Command line ```bash rubocop --require rubocop-packaging ``` ### Rake task ```ruby RuboCop::RakeTask.new do |task| task.requires << 'rubocop-packaging' end ``` ## Development After checking out the repo, run `bin/setup` to install dependencies. Then, run `rake spec` to run the tests. You can also run `bin/console` for an interactive prompt that will allow you to experiment. To install this gem onto your local machine, run `bundle exec rake install`. ## Contributing As always, bug reports and pull requests are heartily welcomed! 💖 This project is intended to be a safe and welcoming space for collaboration. ## License `rubocop-packaging` is available as open-source under the [MIT License](https://github.com/utkarsh2102/rubocop-packaging/blob/master/LICENSE). rubocop-packaging-0.5.1/Rakefile000066400000000000000000000016441374427675300166040ustar00rootroot00000000000000# frozen_string_literal: true require "bundler/gem_tasks" require "rspec/core/rake_task" require "rubocop/rake_task" locallib = File.join(File.dirname(__FILE__), "lib") $LOAD_PATH.unshift locallib Dir["tasks/**/*.rake"].each { |t| load t } RSpec::Core::RakeTask.new(:spec) RuboCop::RakeTask.new task default: %i[ spec rubocop ] desc "Generate a new cop with a template" task :new_cop, [:cop] do |_task, args| require "rubocop" cop_name = args.fetch(:cop) do warn "usage: bundle exec rake new_cop[Department/Name]" exit! end github_user = `git config github.user`.chop github_user = "your_id" if github_user.empty? generator = RuboCop::Cop::Generator.new(cop_name, github_user) generator.write_source generator.write_spec generator.inject_require(root_file_path: "lib/rubocop/cop/packaging_cops.rb") generator.inject_config(config_file_path: "config/default.yml") puts generator.todo end rubocop-packaging-0.5.1/bin/000077500000000000000000000000001374427675300157025ustar00rootroot00000000000000rubocop-packaging-0.5.1/bin/console000077500000000000000000000005771374427675300173030ustar00rootroot00000000000000# frozen_string_literal: true #!/usr/bin/env ruby require "bundler/setup" require "rubocop/packaging" # You can add fixtures and/or initialization code here to make experimenting # with your gem easier. You can also use a different console, if you like. # (If you use this, don't forget to add pry to your Gemfile!) # require "pry" # Pry.start require "irb" IRB.start(__FILE__) rubocop-packaging-0.5.1/bin/setup000077500000000000000000000002041374427675300167640ustar00rootroot00000000000000#!/usr/bin/env bash set -euo pipefail IFS=$'\n\t' set -vx bundle install # Do any other automated setup that you need to do here rubocop-packaging-0.5.1/config/000077500000000000000000000000001374427675300163775ustar00rootroot00000000000000rubocop-packaging-0.5.1/config/default.yml000066400000000000000000000021401374427675300205430ustar00rootroot00000000000000# This is the default configuration file. Packaging/BundlerSetupInTests: Description: >- Using `bundler/setup` in tests is redundant. Consider removing it. Enabled: true VersionAdded: '0.4' VersionChanged: '0.5' Packaging/GemspecGit: Description: >- Avoid using git to produce lists of files. Downstreams often need to build your package in an environment that does not have git (on purpose). Use some pure Ruby alternative, like `Dir` or `Dir.glob`. Enabled: true VersionAdded: '0.1' VersionChanged: '0.1' Packaging/RequireHardcodingLib: Description: >- Avoid using `require` with relative path to lib. Use `require` with absolute path instead. Enabled: true VersionAdded: '0.4' VersionChanged: '0.5' Packaging/RequireRelativeHardcodingLib: Description: >- Avoid using `require_relative` with relative path to lib. Use `require` with absolute path instead. Enabled: true VersionAdded: '0.2' VersionChanged: '0.5' rubocop-packaging-0.5.1/docs/000077500000000000000000000000001374427675300160625ustar00rootroot00000000000000rubocop-packaging-0.5.1/docs/antora.yml000066400000000000000000000001401374427675300200640ustar00rootroot00000000000000name: rubocop-packaging title: RuboCop Packaging version: master nav: - modules/ROOT/nav.adoc rubocop-packaging-0.5.1/docs/modules/000077500000000000000000000000001374427675300175325ustar00rootroot00000000000000rubocop-packaging-0.5.1/docs/modules/ROOT/000077500000000000000000000000001374427675300203155ustar00rootroot00000000000000rubocop-packaging-0.5.1/docs/modules/ROOT/nav.adoc000066400000000000000000000002531374427675300217310ustar00rootroot00000000000000* xref:index.adoc[Home] * xref:installation.adoc[Installation] * xref:usage.adoc[Usage] * xref:cops.adoc[Cops] * Cops Documentation ** xref:cops_packaging.adoc[Packaging] rubocop-packaging-0.5.1/docs/modules/ROOT/pages/000077500000000000000000000000001374427675300214145ustar00rootroot00000000000000rubocop-packaging-0.5.1/docs/modules/ROOT/pages/cops.adoc000066400000000000000000000013521374427675300232110ustar00rootroot00000000000000= Cops In RuboCop lingo, the various checks performed on the code are called "cops". Each cop is responsible for detecting one particular offense. RuboCop Packaging has only one department, called Packaging. == Packaging `Packaging` cops helps both, upstream and downstream maintenance of your projects. // START_COP_LIST === Department xref:cops_packaging.adoc[Packaging] * xref:cops_packaging.adoc#packagingbundlersetupintests[Packaging/BundlerSetupInTests] * xref:cops_packaging.adoc#packaginggemspecgit[Packaging/GemspecGit] * xref:cops_packaging.adoc#packagingrequirehardcodinglib[Packaging/RequireHardcodingLib] * xref:cops_packaging.adoc#packagingrequirerelativehardcodinglib[Packaging/RequireRelativeHardcodingLib] // END_COP_LIST rubocop-packaging-0.5.1/docs/modules/ROOT/pages/cops_packaging.adoc000066400000000000000000000144541374427675300252240ustar00rootroot00000000000000= Packaging == Packaging/BundlerSetupInTests |=== | Enabled by default | Safe | Supports autocorrection | VersionAdded | VersionChanged | Enabled | Yes | Yes | 0.4 | 0.5 |=== This cop flags the `require "bundler/setup"` calls if they're made from inside the tests directory. === Examples [source,ruby] ---- # bad require "foo" require "bundler/setup" # good require "foo" ---- == Packaging/GemspecGit |=== | Enabled by default | Safe | Supports autocorrection | VersionAdded | VersionChanged | Enabled | Yes | No | 0.1 | 0.1 |=== Avoid using `git ls-files` to produce lists of files. Downstreams often need to build your package in an environment that does not have git (on purpose). Instead, use some pure Ruby alternatives, like `Dir` or `Dir.glob`. === Rationale [[gemspec-git-rationale]] Packages in Debian are built in a clean environment (https://wiki.debian.org/sbuild[sbuild], https://wiki.debian.org/Schroot[schroot], et al) and whilst doing so, the build fails with: + `Invalid gemspec in [.gemspec]: No such file or directory - git` And adding `git` as a dependency for each of the Ruby packaging is something that is not right and definitely not recommended. Besides, the source package consists of released tarballs (usually downloaded from GitHub/GitLab releases page or converted from the `.gem` file, obtained using `gem fetch foo`), which is extracted during build. So even if we add `git` as a build dependency, it would still fail as the Debian package source tree is *not a git repo*. Even when the package is maintained in git, it is uploaded as tarballs to the archive without any version control information. Therefore, the only way forward here is to patch out the usage of `git` and use some plain Ruby alternatives like `Dir` or `Dir.glob` or even `Rake::FileList` whilst doing the Debian maintenance. There's not only Debian or other OS packaging situation/examples, but also a couple of others, for instance: * `ruby-core` as part of their CI system runs their test suite against an unpackaged Ruby tarball which doesn't have a `.git` directory. That means they needed to overwrite the bundler gemspec to not use git. + Actually, not anymore, https://github.com/rubygems/bundler/pull/6985[since git has been removed from bundler's gemspec]. * If you build your application on a bare docker image without git, and you are pointing to a git sourced gem that uses git on its gemspec, you'll get: `No such file or directory - git ls-files (Errno::ENOENT)` warnings all around. For example, if you use this in your Gemfile: + `gem "foo", git: "https://github.com/has-git-in-gemspec/foo"` Originally, `git ls-files` inside the default gemspec template was designed so that users publishing their first gem wouldn't unintentionally publish artifacts to it. Recent versions of bundler won't let you release if you have uncommitted files in your working directory, so that risk is lower. === Examples [[gemspec-git-examples]] [source,ruby] ---- # bad Gem::Specification.new do |spec| spec.files = `git ls-files`.split("\n") spec.test_files = `git ls-files -- spec`.split("\n") end # good Gem::Specification.new do |spec| spec.files = Dir["lib/**/*", "LICENSE", "README.md"] spec.test_files = Dir["spec/**/*"] end # bad Gem::Specification.new do |spec| spec.files = Dir.chdir(File.expand_path(__dir__)) do `git ls-files -z`.split("\\x0").reject { |f| f.match(%r{^(test|spec|features)/}) } end end # good require "rake/file_list" Gem::Specification.new do |spec| spec.files = Rake::FileList["**/*"].exclude(*File.read(".gitignore").split) end # bad Gem::Specification.new do |spec| spec.files = `git ls-files -- lib/`.split("\n") spec.test_files = `git ls-files -- test/{functional,unit}/*`.split("\n") spec.executables = `git ls-files -- bin/*`.split("\n").map{ |f| File.basename(f) } end # good Gem::Specification.new do |spec| spec.files = Dir.glob("lib/**/*") spec.test_files = Dir.glob("test/{functional,test}/*") spec.executables = Dir.glob("bin/*").map{ |f| File.basename(f) } end ---- == Packaging/RequireHardcodingLib |=== | Enabled by default | Safe | Supports autocorrection | VersionAdded | VersionChanged | Enabled | Yes | Yes | 0.4 | 0.5 |=== This cop flags the `require` calls, from anywhere mapping to the "lib" directory, except originating from lib/. === Examples [source,ruby] ---- # bad require "../lib/foo/bar" # good require "foo/bar" # bad require File.expand_path("../../lib/foo", __FILE__) # good require "foo" # bad require File.expand_path("../../../lib/foo/bar/baz/qux", __dir__) # good require "foo/bar/baz/qux" # bad require File.dirname(__FILE__) + "/../../lib/baz/qux" # good require "baz/qux" ---- == Packaging/RequireRelativeHardcodingLib |=== | Enabled by default | Safe | Supports autocorrection | VersionAdded | VersionChanged | Enabled | Yes | Yes | 0.2 | 0.5 |=== Avoid using `require_relative` with relative path to lib. Use `require` instead. === Rationale [[require-relative-hardcoding-lib-rationale]] Debian has a https://ci.debian.net/[testing infrastructure] that is designed to test packages in their installed form, i.e., closer to how an end-user would use it than to how a developer working against it. For this to work, the test-suite must load code that's installed system-wide, instead of the code in the source tree. Using `require_relative` from the tests into the `lib` directory makes that impossible, but it also makes the test look less like client-code that would use the code in your gem. Therefore, we recommend that test code uses the main library code without `require_relative`. Therefore, when one uses a relative path, we end up getting a `LoadError`, stating: + `cannot load such file -- /<>/foo`. We want to emphasize that *there is nothing wrong* with using `require_relative` inside `lib/`, it's just using it from your test code to the `lib` directory prevents the "test the library installed system-wide" use case. Therefore, it is still recommended to use `require_relative` with just this exception to it. === Examples [[require-relative-hardcoding-lib-examples]] [source,ruby] ---- # bad require_relative "lib/foo" # good require "foo" # bad require_relative "../../lib/foo/bar" # good require "foo/bar" # good require_relative "foo/bar/bax" require_relative "baz/qux" ---- rubocop-packaging-0.5.1/docs/modules/ROOT/pages/index.adoc000066400000000000000000000026011374427675300233520ustar00rootroot00000000000000= RuboCop Packaging `RuboCop::Packaging` is an extension of https://rubocop.org[RuboCop], which is a Ruby static code analyzer (a.k.a. linter) and code formatter. It helps to enforce some of the guidelines that are expected of upstream maintainers so that the downstream can build their packages in a clean environment without any problems. == Why Packaging Extension? The Debian Ruby team has a lot of experience in packaging and maintaining Ruby libraries and applications for Debian. During this work, they identified several issues in upstream codebases that make it difficult to build a Debian package straight out of those Ruby gems (shipped via https://rubygems.org[RubyGems]). The Debian developers (downstream maintainers) have been in touch with the RubyGems & Bundler and other upstream maintainers and we're collaborating to make things easier for OS packagers while not compromising the experience for upstream maintainers. As a result, we're working on this RuboCop extension to enforce a set of best practices that upstream maintainers can follow to make the lives of packagers easier. And that is how `rubocop-packaging` is born! https://packaging.rubystyle.guide[The Packaging Style Guide] lists the cops with some good and bad examples and the rationale behind these cops can be found in the https://docs.rubocop.org/rubocop-packaging/cops_packaging.html[Cops Documentation] section. rubocop-packaging-0.5.1/docs/modules/ROOT/pages/installation.adoc000066400000000000000000000005741374427675300247530ustar00rootroot00000000000000= Installation Add this line to your application's Gemfile: [source,ruby] ---- gem 'rubocop-packaging', require: false ---- And then execute: [source,bash] ---- $ bundle install ---- Or install it yourself as: [source,bash] ---- $ gem install rubocop-packaging ---- Or on Debian based systems, you can also do: [source,bash] ---- $ apt install ruby-rubocop-packaging ---- rubocop-packaging-0.5.1/docs/modules/ROOT/pages/usage.adoc000066400000000000000000000013041374427675300233460ustar00rootroot00000000000000= Usage You need to tell RuboCop to load the Packaging extension. There are three ways to do this: == RuboCop configuration file Put this into your `.rubocop.yml` file: [source,yaml] ---- require: rubocop-packaging ---- Alternatively, use the following array notation when specifying multiple extensions: [source,yaml] ---- require: - rubocop-other-extension - rubocop-packaging ---- Now you can run `rubocop` and it will automatically load the RuboCop Packaging cops together with the standard cops. == Command line [source,bash] ---- rubocop --require rubocop-packaging ---- == Rake task [source,ruby] ---- RuboCop::RakeTask.new do |task| task.requires << 'rubocop-packaging' end ---- rubocop-packaging-0.5.1/lib/000077500000000000000000000000001374427675300157005ustar00rootroot00000000000000rubocop-packaging-0.5.1/lib/rubocop-packaging.rb000066400000000000000000000004051374427675300216170ustar00rootroot00000000000000# frozen_string_literal: true require "rubocop" require_relative "rubocop/packaging" require_relative "rubocop/packaging/version" require_relative "rubocop/packaging/inject" RuboCop::Packaging::Inject.defaults! require_relative "rubocop/cop/packaging_cops" rubocop-packaging-0.5.1/lib/rubocop/000077500000000000000000000000001374427675300173515ustar00rootroot00000000000000rubocop-packaging-0.5.1/lib/rubocop/cop/000077500000000000000000000000001374427675300201325ustar00rootroot00000000000000rubocop-packaging-0.5.1/lib/rubocop/cop/packaging/000077500000000000000000000000001374427675300220565ustar00rootroot00000000000000rubocop-packaging-0.5.1/lib/rubocop/cop/packaging/bundler_setup_in_tests.rb000066400000000000000000000051701374427675300271710ustar00rootroot00000000000000# frozen_string_literal: true require "rubocop/packaging/lib_helper_module" module RuboCop # :nodoc: module Cop # :nodoc: module Packaging # :nodoc: # This cop flags the `require "bundler/setup"` calls if they're # made from inside the tests directory. # # @example # # # bad # require "foo" # require "bundler/setup" # # # good # require "foo" # class BundlerSetupInTests < Base include RuboCop::Packaging::LibHelperModule include RangeHelp extend AutoCorrector # This is the message that will be displayed when RuboCop::Packaging finds # an offense of using `require "bundler/setup"` in the tests directory. MSG = "Using `bundler/setup` in tests is redundant. Consider removing it." def_node_matcher :bundler_setup?, <<~PATTERN (send nil? :require (str #bundler_setup_in_test_dir?)) PATTERN # Extended from the Base class. # More about the `#on_new_investigation` method can be found here: # https://github.com/rubocop-hq/rubocop/blob/343f62e4555be0470326f47af219689e21c61a37/lib/rubocop/cop/base.rb # # Processing of the AST happens here. def on_new_investigation @file_path = processed_source.file_path @file_directory = File.dirname(@file_path) end # Extended from AST::Traversal. # More about the `#on_send` method can be found here: # https://github.com/rubocop-hq/rubocop-ast/blob/08d0f49a47af1e9a30a6d8f67533ba793c843d67/lib/rubocop/ast/traversal.rb#L112 def on_send(node) return unless bundler_setup?(node) add_offense(node) do |corrector| autocorrect(corrector, node) end end # Called from on_send, this method helps to autocorrect # the offenses flagged by this cop. def autocorrect(corrector, node) range = range_by_whole_lines(node.source_range, include_final_newline: true) corrector.remove(range) end # This method is called from inside `#def_node_matcher`. # It flags an offense if the `require "bundler/setup"` # call is made from the tests directory. def bundler_setup_in_test_dir?(str) str.eql?("bundler/setup") && falls_in_test_dir? end # This method determines if the call is made *from* the tests directory. def falls_in_test_dir? %w[spec specs test tests].any? { |dir| File.expand_path(@file_directory).start_with?("#{root_dir}/#{dir}") } end end end end end rubocop-packaging-0.5.1/lib/rubocop/cop/packaging/gemspec_git.rb000066400000000000000000000063261374427675300247000ustar00rootroot00000000000000# frozen_string_literal: true module RuboCop # :nodoc: module Cop # :nodoc: module Packaging # :nodoc: # This cop flags the usage of `git ls-files` in gemspec # and suggests to use a plain Ruby alternative, like `Dir`, # `Dir.glob`, or `Rake::FileList` instead. # # @example # # # bad # Gem::Specification.new do |spec| # spec.files = `git ls-files`.split("\n") # spec.test_files = `git ls-files -- spec`.split("\n") # end # # # good # Gem::Specification.new do |spec| # spec.files = Dir["lib/**/*", "LICENSE", "README.md"] # spec.test_files = Dir["spec/**/*"] # end # # # bad # Gem::Specification.new do |spec| # spec.files = Dir.chdir(File.expand_path(__dir__)) do # `git ls-files -z`.split("\\x0").reject { |f| f.match(%r{^(test|spec|features)/}) } # end # end # # # good # require "rake/file_list" # # Gem::Specification.new do |spec| # spec.files = Rake::FileList["**/*"].exclude(*File.read(".gitignore").split) # end # # # bad # Gem::Specification.new do |spec| # spec.files = `git ls-files -- lib/`.split("\n") # spec.test_files = `git ls-files -- test/{functional,unit}/*`.split("\n") # spec.executables = `git ls-files -- bin/*`.split("\n").map{ |f| File.basename(f) } # end # # # good # Gem::Specification.new do |spec| # spec.files = Dir.glob("lib/**/*") # spec.test_files = Dir.glob("test/{functional,test}/*") # spec.executables = Dir.glob("bin/*").map{ |f| File.basename(f) } # end # class GemspecGit < Base # This is the message that will be displayed when RuboCop finds an # offense of using `git ls-files`. MSG = "Avoid using git to produce lists of files. " \ "Downstreams often need to build your package in an environment " \ "that does not have git (on purpose). " \ "Use some pure Ruby alternative, like `Dir` or `Dir.glob`." def_node_search :xstr, <<~PATTERN (block (send (const (const {cbase nil?} :Gem) :Specification) :new) (args (arg _)) `$(xstr (str #starts_with_git?))) PATTERN # Extended from the Cop class. # More about the `#investigate` method can be found here: # https://github.com/rubocop-hq/rubocop/blob/59543c8e2b66bff249de131fa9105f3eb11e9edb/lib/rubocop/cop/cop.rb#L13-L25 # # Processing of the AST happens here. def on_new_investigation return if processed_source.blank? xstr(processed_source.ast).each do |node| add_offense( node.loc.expression, message: MSG ) end end # This method is called from inside `#def_node_search`. # It is used to find strings which start with "git". def starts_with_git?(str) str.start_with?("git") end end end end end rubocop-packaging-0.5.1/lib/rubocop/cop/packaging/require_hardcoding_lib.rb000066400000000000000000000077561374427675300271060ustar00rootroot00000000000000# frozen_string_literal: true require "rubocop/packaging/lib_helper_module" module RuboCop # :nodoc: module Cop # :nodoc: module Packaging # :nodoc: # This cop flags the `require` calls, from anywhere mapping to # the "lib" directory, except originating from lib/. # # @example # # # bad # require "../lib/foo/bar" # # # good # require "foo/bar" # # # bad # require File.expand_path("../../lib/foo", __FILE__) # # # good # require "foo" # # # bad # require File.expand_path("../../../lib/foo/bar/baz/qux", __dir__) # # # good # require "foo/bar/baz/qux" # # # bad # require File.dirname(__FILE__) + "/../../lib/baz/qux" # # # good # require "baz/qux" # class RequireHardcodingLib < Base include RuboCop::Packaging::LibHelperModule extend AutoCorrector # This is the message that will be displayed when RuboCop::Packaging # finds an offense of using `require` with relative path to lib. MSG = "Avoid using `require` with relative path to `lib/`. " \ "Use `require` with absolute path instead." def_node_matcher :require?, <<~PATTERN {(send nil? :require (str #falls_in_lib?)) (send nil? :require (send (const nil? :File) :expand_path (str #falls_in_lib?) (send nil? :__dir__))) (send nil? :require (send (const nil? :File) :expand_path (str #falls_in_lib_using_file?) (str _))) (send nil? :require (send (send (const nil? :File) :dirname {(str _) (send nil? _)}) :+ (str #falls_in_lib_with_file_dirname_plus_str?))) (send nil? :require (dstr (begin (send (const nil? :File) :dirname {(str _) (send nil? _)})) (str #falls_in_lib_with_file_dirname_plus_str?)))} PATTERN # Extended from the Base class. # More about the `#on_new_investigation` method can be found here: # https://github.com/rubocop-hq/rubocop/blob/343f62e4555be0470326f47af219689e21c61a37/lib/rubocop/cop/base.rb # # Processing of the AST happens here. def on_new_investigation @file_path = processed_source.file_path @file_directory = File.dirname(@file_path) end # Extended from AST::Traversal. # More about the `#on_send` method can be found here: # https://github.com/rubocop-hq/rubocop-ast/blob/08d0f49a47af1e9a30a6d8f67533ba793c843d67/lib/rubocop/ast/traversal.rb#L112 def on_send(node) return unless require?(node) add_offense(node) do |corrector| corrector.replace(node, good_require_call) end end # Called from on_send, this method helps to replace # the "bad" require call with the "good" one. def good_require_call good_call = @str.sub(%r{^.*/lib/}, "") %(require "#{good_call}") end # This method is called from inside `#def_node_matcher`. # It flags an offense if the `require` call is made from # anywhere except the "lib" directory. def falls_in_lib?(str) @str = str target_falls_in_lib?(str) && inspected_file_is_not_in_lib_or_gemspec? end # This method is called from inside `#def_node_matcher`. # It flags an offense if the `require` call (using the __FILE__ # arguement) is made from anywhere except the "lib" directory. def falls_in_lib_using_file?(str) @str = str target_falls_in_lib_using_file?(str) && inspected_file_is_not_in_lib_or_gemspec? end # This method preprends a "." to the string that starts with "/". # And then determines if that call is made to "lib/". def falls_in_lib_with_file_dirname_plus_str?(str) @str = str str.prepend(".") target_falls_in_lib?(str) && inspected_file_is_not_in_lib_or_gemspec? end end end end end rubocop-packaging-0.5.1/lib/rubocop/cop/packaging/require_relative_hardcoding_lib.rb000066400000000000000000000054011374427675300307620ustar00rootroot00000000000000# frozen_string_literal: true require "rubocop/packaging/lib_helper_module" module RuboCop # :nodoc: module Cop # :nodoc: module Packaging # :nodoc: # This cop flags the `require_relative` calls, from anywhere # mapping to the "lib" directory, except originating from lib/ or # the gemspec file, and suggests to use `require` instead. # # @example # # # bad # require_relative "lib/foo" # # # good # require "foo" # # # bad # require_relative "../../lib/foo/bar" # # # good # require "foo/bar" # # # good # require_relative "foo/bar/bax" # require_relative "baz/qux" # class RequireRelativeHardcodingLib < Base include RuboCop::Packaging::LibHelperModule extend AutoCorrector # This is the message that will be displayed when RuboCop finds an # offense of using `require_relative` with relative path to lib. MSG = "Avoid using `require_relative` with relative path to lib. " \ "Use `require` with absolute path instead." def_node_matcher :require_relative, <<~PATTERN (send nil? :require_relative (str #falls_in_lib?)) PATTERN # Extended from the Base class. # More about the `#on_new_investigation` method can be found here: # https://github.com/rubocop-hq/rubocop/blob/343f62e4555be0470326f47af219689e21c61a37/lib/rubocop/cop/base.rb # # Processing of the AST happens here. def on_new_investigation @file_path = processed_source.file_path @file_directory = File.dirname(@file_path) end # Extended from AST::Traversal. # More about the `#on_send` method can be found here: # https://github.com/rubocop-hq/rubocop-ast/blob/08d0f49a47af1e9a30a6d8f67533ba793c843d67/lib/rubocop/ast/traversal.rb#L112 def on_send(node) return unless require_relative(node) add_offense(node) do |corrector| corrector.replace(node, good_require_call) end end # Called from on_send, this method helps to replace the # "bad" require_relative call with the "good" one. def good_require_call good_call = File.expand_path(@str, @file_directory).delete_prefix("#{root_dir}/lib/") %(require "#{good_call}") end # This method is called from inside `#def_node_matcher`. # It flags an offense if the `require_relative` call is made # from anywhere except the "lib" directory. def falls_in_lib?(str) @str = str target_falls_in_lib?(str) && inspected_file_is_not_in_lib_or_gemspec? end end end end end rubocop-packaging-0.5.1/lib/rubocop/cop/packaging_cops.rb000066400000000000000000000003551374427675300234320ustar00rootroot00000000000000# frozen_string_literal: true require_relative "packaging/bundler_setup_in_tests" require_relative "packaging/gemspec_git" require_relative "packaging/require_hardcoding_lib" require_relative "packaging/require_relative_hardcoding_lib" rubocop-packaging-0.5.1/lib/rubocop/packaging.rb000066400000000000000000000006411374427675300216230ustar00rootroot00000000000000# frozen_string_literal: true require "rubocop/packaging/version" module RuboCop # RuboCop Packaging project namespace module Packaging PROJECT_ROOT = Pathname.new(__dir__).parent.parent.expand_path.freeze CONFIG_DEFAULT = PROJECT_ROOT.join("config", "default.yml").freeze CONFIG = YAML.safe_load(CONFIG_DEFAULT.read).freeze private_constant(:CONFIG_DEFAULT, :PROJECT_ROOT) end end rubocop-packaging-0.5.1/lib/rubocop/packaging/000077500000000000000000000000001374427675300212755ustar00rootroot00000000000000rubocop-packaging-0.5.1/lib/rubocop/packaging/inject.rb000066400000000000000000000011561374427675300231010ustar00rootroot00000000000000# frozen_string_literal: true module RuboCop module Packaging # Because RuboCop doesn't yet support plugins, we have to monkey patch in a # bit of our configuration. module Inject def self.defaults! path = CONFIG_DEFAULT.to_s hash = ConfigLoader.send(:load_yaml_configuration, path) config = Config.new(hash, path).tap(&:make_excludes_absolute) puts "configuration from #{path}" if ConfigLoader.debug? config = ConfigLoader.merge_with_default(config, path) ConfigLoader.instance_variable_set(:@default_configuration, config) end end end end rubocop-packaging-0.5.1/lib/rubocop/packaging/lib_helper_module.rb000066400000000000000000000025441374427675300253010ustar00rootroot00000000000000# frozen_string_literal: true module RuboCop # :nodoc: module Packaging # :nodoc: # This helper module extracts the methods which can be used # in other cop classes. module LibHelperModule # For determining the root directory of the project. def root_dir RuboCop::ConfigLoader.project_root end # This method determines if the calls are made to the "lib" directory. def target_falls_in_lib?(str) File.expand_path(str, @file_directory).start_with?("#{root_dir}/lib") end # This method determines if the calls (using the __FILE__ argument) # are made to the "lib" directory. def target_falls_in_lib_using_file?(str) File.expand_path(str, @file_path).start_with?("#{root_dir}/lib") end # This method determines if that call is made *from* the "lib" directory. def inspected_file_falls_in_lib? @file_path.start_with?("#{root_dir}/lib") end # This method determines if that call is made *from* the "gemspec" file. def inspected_file_is_gemspec? @file_path.end_with?("gemspec") end # This method determines if the inspected file is not in lib/ or # isn't a gemspec file. def inspected_file_is_not_in_lib_or_gemspec? !inspected_file_falls_in_lib? && !inspected_file_is_gemspec? end end end end rubocop-packaging-0.5.1/lib/rubocop/packaging/version.rb000066400000000000000000000001411374427675300233030ustar00rootroot00000000000000# frozen_string_literal: true module RuboCop module Packaging VERSION = "0.5.1" end end rubocop-packaging-0.5.1/rubocop-packaging.gemspec000066400000000000000000000024221374427675300220720ustar00rootroot00000000000000# frozen_string_literal: true require_relative "lib/rubocop/packaging/version" Gem::Specification.new do |spec| spec.name = "rubocop-packaging" spec.version = RuboCop::Packaging::VERSION spec.authors = ["Utkarsh Gupta"] spec.email = ["utkarsh@debian.org"] spec.license = "MIT" spec.homepage = "https://github.com/utkarsh2102/rubocop-packaging" spec.summary = "Automatic downstream compatability checking tool for Ruby code" spec.description = <<~DESCRIPTION A collection of RuboCop cops to check for downstream compatability issues in the Ruby code. DESCRIPTION spec.metadata["homepage_uri"] = spec.homepage spec.metadata["source_code_uri"] = "https://github.com/utkarsh2102/rubocop-packaging" spec.files = Dir["config/default.yml", "lib/**/*", "LICENSE", "README.md"] spec.require_paths = ["lib"] spec.required_ruby_version = Gem::Requirement.new(">= 2.4.0") spec.add_development_dependency "bump", "~> 0.8" spec.add_development_dependency "pry", "~> 0.13" spec.add_development_dependency "rake", "~> 13.0" spec.add_development_dependency "rspec", "~> 3.0" spec.add_development_dependency "yard", "~> 0.9" spec.add_runtime_dependency "rubocop", ">= 0.89", "< 2.0" end rubocop-packaging-0.5.1/spec/000077500000000000000000000000001374427675300160645ustar00rootroot00000000000000rubocop-packaging-0.5.1/spec/rubocop/000077500000000000000000000000001374427675300175355ustar00rootroot00000000000000rubocop-packaging-0.5.1/spec/rubocop/cop/000077500000000000000000000000001374427675300203165ustar00rootroot00000000000000rubocop-packaging-0.5.1/spec/rubocop/cop/packaging/000077500000000000000000000000001374427675300222425ustar00rootroot00000000000000rubocop-packaging-0.5.1/spec/rubocop/cop/packaging/bundler_setup_in_tests_spec.rb000066400000000000000000000030561374427675300303700ustar00rootroot00000000000000# frozen_string_literal: true RSpec.describe RuboCop::Cop::Packaging::BundlerSetupInTests, :config do let(:message) { RuboCop::Cop::Packaging::BundlerSetupInTests::MSG } let(:project_root) { RuboCop::ConfigLoader.project_root } context "when `require bundler/setup` is used in specs/" do let(:filename) { "#{project_root}/spec/spec_helper.rb" } let(:source) { "require 'bundler/setup'" } it "registers an offense" do expect_offense(<<~RUBY, filename) #{source} #{"^" * source.length} #{message} RUBY expect_correction(<<~RUBY) RUBY end end context "when `require bundler/setup` is used in test/foo" do let(:filename) { "#{project_root}/tests/foo/test_bar.rb" } let(:source) { "require 'bundler/setup'" } it "registers an offense" do expect_offense(<<~RUBY, filename) #{source} #{"^" * source.length} #{message} RUBY expect_correction(<<~RUBY) RUBY end end context "when `require bundler/setup` is used in a Rakefile" do let(:filename) { "#{project_root}/Rakefile" } let(:source) { "require 'bundler/setup'" } it "does not register an offense" do expect_no_offenses(<<~RUBY, filename) #{source} RUBY end end context "when `require bundler/setup` is used in bin/console" do let(:filename) { "#{project_root}/bin/console" } let(:source) { "require 'bundler/setup'" } it "does not register an offense" do expect_no_offenses(<<~RUBY, filename) #{source} RUBY end end end rubocop-packaging-0.5.1/spec/rubocop/cop/packaging/gemspec_git_spec.rb000066400000000000000000000061171374427675300260740ustar00rootroot00000000000000# frozen_string_literal: true RSpec.describe RuboCop::Cop::Packaging::GemspecGit do subject(:cop) { described_class.new(config) } let(:config) { RuboCop::Config.new } let(:message) { RuboCop::Cop::Packaging::GemspecGit::MSG } it "registers an offense when using `git` for :files=" do expect_offense(<<~RUBY) Gem::Specification.new do |spec| spec.files = `git ls-files`.split("\\n") ^^^^^^^^^^^^^^ #{message} end RUBY end it "registers an offense when using `git ls-files filename` for :files=" do expect_offense(<<~RUBY) Gem::Specification.new do |s| s.files = `git ls-files LICENSE docs lib`.split("\\n") ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ #{message} end RUBY end it "registers an offense when using `git` for :files= but differently" do expect_offense(<<~RUBY) Gem::Specification.new do |spec| spec.files = `git ls-files`.split + %w( ^^^^^^^^^^^^^^ #{message} lib/parser/lexer.rb lib/parser/ruby18.rb lib/parser/ruby19.rb lib/parser/ruby20.rb lib/parser/ruby21.rb lib/parser/ruby22.rb lib/parser/ruby23.rb lib/parser/ruby24.rb lib/parser/ruby25.rb lib/parser/ruby26.rb lib/parser/ruby27.rb lib/parser/ruby28.rb lib/parser/macruby.rb lib/parser/rubymotion.rb ) end RUBY end it "registers an offense when using `git` for :files= with more stuff" do expect_offense(<<~RUBY) Gem::Specification.new do |spec| spec.files = Dir.chdir(File.expand_path("..", __FILE__)) do `git ls-files -z`.split("\\x0").reject { |f| f.match(%r{^(test|spec|features)/}) } ^^^^^^^^^^^^^^^^^ #{message} end end RUBY end it "registers an offense when using `git` for :executables=" do expect_offense(<<~RUBY) Gem::Specification.new do |spec| spec.executables = `git ls-files`.split("\\n") ^^^^^^^^^^^^^^ #{message} end RUBY end it "does not register an offense when the file just has comments" do expect_no_offenses(<<~RUBY) # Dummy comments. # Blank file. # Copyright 2020, Utkarsh Gupta # This is an important test. RUBY end it "does not register an offense when the file is empty/blank" do expect_no_offenses(<<~RUBY) RUBY end it "does not register an offense not in a specification" do expect_no_offenses(<<~RUBY) spec.files = `git ls-files` RUBY end it "does not register an offense when not using `git` for :files=" do expect_no_offenses(<<~RUBY) Gem::Specification.new do |spec| spec.files = Dir["docs/**/*", "lib/**/*", "LICENSE"].reject { |f| File.directory?(f) }.sort end RUBY end end rubocop-packaging-0.5.1/spec/rubocop/cop/packaging/require_hardcoding_lib_spec.rb000066400000000000000000000214201374427675300302640ustar00rootroot00000000000000# frozen_string_literal: true RSpec.describe RuboCop::Cop::Packaging::RequireHardcodingLib, :config do let(:message) { RuboCop::Cop::Packaging::RequireHardcodingLib::MSG } let(:project_root) { RuboCop::ConfigLoader.project_root } context "when `require` call lies outside spec/" do let(:filename) { "#{project_root}/spec/foo_spec.rb" } let(:source) { "require '../lib/foo.rb'" } it "registers an offense" do expect_offense(<<~RUBY, filename) #{source} #{"^" * source.length} #{message} RUBY expect_correction(<<~RUBY) require "foo.rb" RUBY end end context "when `require` call after using `unshift` lies outside spec/" do let(:filename) { "#{project_root}/tests/foo/bar.rb" } let(:source) { <<~RUBY.chomp } $:.unshift('../../lib') require '../../lib/foo/bar' RUBY it "registers an offense" do expect_offense(<<~RUBY, filename) #{source} #{"^" * 27} #{message} RUBY expect_correction(<<~RUBY) $:.unshift('../../lib') require "foo/bar" RUBY end end context "when `require` call uses File#expand_path method with __FILE__" do let(:filename) { "#{project_root}/spec/foo.rb" } let(:source) { "require File.expand_path('../../lib/foo', __FILE__)" } it "registers an offense" do expect_offense(<<~RUBY, filename) #{source} #{"^" * source.length} #{message} RUBY expect_correction(<<~RUBY) require "foo" RUBY end end context "when `require` call uses File#expand_path method with __dir__" do let(:filename) { "#{project_root}/test/foo/bar/qux_spec.rb" } let(:source) { "require File.expand_path('../../../lib/foo/bar/baz/qux', __dir__)" } it "registers an offense" do expect_offense(<<~RUBY, filename) #{source} #{"^" * source.length} #{message} RUBY expect_correction(<<~RUBY) require "foo/bar/baz/qux" RUBY end end context "when `require` call uses File#dirname method with __FILE__" do let(:filename) { "#{project_root}/specs/baz/qux_spec.rb" } let(:source) { "require File.dirname(__FILE__) + '/../../lib/baz/qux'" } it "registers an offense" do expect_offense(<<~RUBY, filename) #{source} #{"^" * source.length} #{message} RUBY expect_correction(<<~RUBY) require "baz/qux" RUBY end end context "when `require` call uses File#dirname method with __FILE__ with interpolation" do let(:filename) { "#{project_root}/specs/baz/qux_spec.rb" } let(:source) { 'require "#{File.dirname(__FILE__)}/../../lib/baz/qux"' } # rubocop:disable Lint/InterpolationCheck it "registers an offense" do expect_offense(<<~RUBY, filename) #{source} #{"^" * source.length} #{message} RUBY expect_correction(<<~RUBY) require "baz/qux" RUBY end end context "when `require` call uses File#dirname method with __dir__" do let(:filename) { "#{project_root}/spec/foo.rb" } let(:source) { "require File.dirname(__dir__) + '/../lib/foo'" } it "registers an offense" do expect_offense(<<~RUBY, filename) #{source} #{"^" * source.length} #{message} RUBY expect_correction(<<~RUBY) require "foo" RUBY end end context "when `require` call uses File#dirname method with __dir__ with interpolation" do let(:filename) { "#{project_root}/spec/foo.rb" } let(:source) { 'require "#{File.dirname(__dir__)}/../lib/foo"' } # rubocop:disable Lint/InterpolationCheck it "registers an offense" do expect_offense(<<~RUBY, filename) #{source} #{"^" * source.length} #{message} RUBY expect_correction(<<~RUBY) require "foo" RUBY end end context "when the `require` call doesn't use relative path" do let(:filename) { "#{project_root}/spec/bar_spec.rb" } let(:source) { "require 'bar'" } it "does not register an offense" do expect_no_offenses(<<~RUBY, filename) #{source} RUBY end end context "when the `require` call lies inside test/" do let(:filename) { "#{project_root}/test/bar/foo_spec.rb" } let(:source) { "require '../foo'" } it "does not register an offense" do expect_no_offenses(<<~RUBY, filename) #{source} RUBY end end context "when the `require` call is made to lib/ but it lies under spec/" do let(:filename) { "#{project_root}/spec/lib/baz/qux_spec.rb" } let(:source) { "require '../../lib/foo'" } it "does not register an offense" do expect_no_offenses(<<~RUBY, filename) #{source} RUBY end end context "when the `require` call is made to lib/ from inside lib/ itself" do let(:filename) { "#{project_root}/lib/foo/bar.rb" } let(:source) { "require '../foo'" } it "does not register an offense" do expect_no_offenses(<<~RUBY, filename) #{source} RUBY end end context "when the `require` call is made from the gemspec file" do let(:filename) { "#{project_root}/foo.gemspec" } let(:source) { "require 'lib/foo/version'" } it "does not register an offense" do expect_no_offenses(<<~RUBY, filename) #{source} RUBY end end context "when `require` call uses File#expand_path method with __FILE__ but lies inside lib/" do let(:filename) { "#{project_root}/lib/foo/bar.rb" } let(:source) { "require File.expand_path('../../foo', __FILE__)" } it "does not register an offense" do expect_no_offenses(<<~RUBY, filename) #{source} RUBY end end context "when `require` call uses File#expand_path method with __FILE__ and is made from the gemspec file" do let(:filename) { "#{project_root}/bar.gemspec" } let(:source) { "require File.expand_path('../lib/foo/version', __FILE__)" } it "does not register an offense" do expect_no_offenses(<<~RUBY, filename) #{source} RUBY end end context "when `require` call uses File#expand_path method with __dir__ but lies inside lib/" do let(:filename) { "#{project_root}/lib/foo/bar/baz/qux.rb" } let(:source) { "require File.expand_path('../../foo', __dir__)" } it "does not register an offense" do expect_no_offenses(<<~RUBY, filename) #{source} RUBY end end context "when `require` call uses File#expand_path method with __dir__ and is made from the gemspec file" do let(:filename) { "#{project_root}/qux.gemspec" } let(:source) { "require File.expand_path('lib/qux/version', __dir__)" } it "does not register an offense" do expect_no_offenses(<<~RUBY, filename) #{source} RUBY end end context "when the `require` call uses File#dirname with __FILE__ but lies inside tests/" do let(:filename) { "#{project_root}/tests/foo/bar_spec.rb" } let(:source) { "require File.dirname(__FILE__) + '/../lib/bar'" } it "does not register an offense" do expect_no_offenses(<<~RUBY, filename) #{source} RUBY end end context "when the `require` call uses File#dirname with __FILE__ but lies inside lib/" do let(:filename) { "#{project_root}/lib/baz/qux.rb" } let(:source) { "require File.dirname(__FILE__) + '/../baz'" } it "does not register an offense" do expect_no_offenses(<<~RUBY, filename) #{source} RUBY end end context "when the `require` call uses File#dirname with __FILE__ and is made from the gemspec file" do let(:filename) { "#{project_root}/bar.gemspec" } let(:source) { "require File.dirname(__FILE__) + '/../lib/bar/version'" } it "does not register an offense" do expect_no_offenses(<<~RUBY, filename) #{source} RUBY end end context "when the `require` call uses File#dirname with __dir__ but lies inside spec/" do let(:filename) { "#{project_root}/spec/foo/bar_spec.rb" } let(:source) { "require File.dirname(__dir__) + '/../lib/bar'" } it "does not register an offense" do expect_no_offenses(<<~RUBY, filename) #{source} RUBY end end context "when the `require` call uses File#dirname with __dir__ but lies inside lib/" do let(:filename) { "#{project_root}/lib/baz/qux.rb" } let(:source) { "require File.dirname(__dir__) + '/../baz'" } it "does not register an offense" do expect_no_offenses(<<~RUBY, filename) #{source} RUBY end end context "when the `require` call uses File#dirname with __dir__ and is made from the gemspec file" do let(:filename) { "#{project_root}/baz.gemspec" } let(:source) { "require File.dirname(__dir__) + '/lib/baz/version'" } it "does not register an offense" do expect_no_offenses(<<~RUBY, filename) #{source} RUBY end end end rubocop-packaging-0.5.1/spec/rubocop/cop/packaging/require_relative_hardcoding_lib_spec.rb000066400000000000000000000077221374427675300321700ustar00rootroot00000000000000# frozen_string_literal: true RSpec.describe RuboCop::Cop::Packaging::RequireRelativeHardcodingLib, :config do let(:message) { RuboCop::Cop::Packaging::RequireRelativeHardcodingLib::MSG } let(:project_root) { RuboCop::ConfigLoader.project_root } context "when `require_relative` call lies outside spec/" do let(:filename) { "#{project_root}/spec/foo_spec.rb" } let(:source) { "require_relative '../lib/foo.rb'" } it "registers an offense" do expect_offense(<<~RUBY, filename) #{source} #{"^" * source.length} #{message} RUBY expect_correction(<<~RUBY) require "foo.rb" RUBY end end context "when `require_relative` call with nested path lies outside test/" do let(:filename) { "#{project_root}/test/rubocop/cop/bar_spec.rb" } let(:source) { "require_relative '../../../lib/bar'" } it "registers an offense" do expect_offense(<<~RUBY, filename) #{source} #{"^" * source.length} #{message} RUBY expect_correction(<<~RUBY) require "bar" RUBY end end context "when one `require_relative` call lies outside specs/" do let(:filename) { "#{project_root}/specs/baz_spec.rb" } let(:source) { <<~RUBY.chomp } require_relative "spec_helper" require_relative "../lib/rubocop/baz" RUBY it "registers an offense" do expect_offense(<<~RUBY, filename) #{source} #{"^" * 37} #{message} RUBY expect_correction(<<~RUBY) require_relative "spec_helper" require "rubocop/baz" RUBY end end context "when `require_relative` call with `unshift` lies outside tests/" do let(:filename) { "#{project_root}/tests/qux_spec.rb" } let(:source) { <<~RUBY.chomp } $:.unshift("../lib") require_relative "../lib/qux" RUBY it "registers an offense" do expect_offense(<<~RUBY, filename) #{source} #{"^" * 29} #{message} RUBY expect_correction(<<~RUBY) $:.unshift("../lib") require "qux" RUBY end end context "when `require_relative` call is made from inside lib/" do let(:filename) { "#{project_root}/lib/foo.rb" } let(:source) { "require_relative '../lib/bar'" } it "does not register an offense" do expect_no_offenses(<<~RUBY, filename) #{source} RUBY end end context "when `require_relative` call is made from the gemspec file" do let(:filename) { "#{project_root}/foo.gemspec" } let(:source) { "require_relative 'lib/foo/version'" } it "does not register an offense" do expect_no_offenses(<<~RUBY, filename) #{source} RUBY end end context "when `require_relative` calls are made from inside lib/" do let(:filename) { "#{project_root}/lib/foo/bar.rb" } let(:source) { <<~RUBY.chomp } require_relative "../baz" require_relative "foo/qux" RUBY it "does not register an offense" do expect_no_offenses(<<~RUBY, filename) #{source} RUBY end end context "when the `require_relative` call to `lib` lies inside spec/" do let(:filename) { "#{project_root}/spec/rubocop/cop/foo_spec.rb" } let(:source) { "require_relative '../lib/foo'" } it "does not register an offense" do expect_no_offenses(<<~RUBY, filename) #{source} RUBY end end context "when the `require_relative` call lies inside tests/" do let(:filename) { "#{project_root}/tests/rubocop/cop/bar_spec.rb" } let(:source) { "require_relative '../bar'" } it "does not register an offense" do expect_no_offenses(<<~RUBY, filename) #{source} RUBY end end context "when the `require_relative` call lies inside test/" do let(:filename) { "#{project_root}/test/qux_spec.rb" } let(:source) { "require_relative 'spec/rubocop/qux.rb'" } it "does not register an offense" do expect_no_offenses(<<~RUBY, filename) #{source} RUBY end end end rubocop-packaging-0.5.1/spec/rubocop/packaging_spec.rb000066400000000000000000000052471374427675300230300ustar00rootroot00000000000000# frozen_string_literal: true RSpec.describe RuboCop::Packaging do describe "general structure" do it "has a version number" do expect(RuboCop::Packaging::VERSION).not_to be nil end end describe "default configuration file" do subject(:config) { RuboCop::ConfigLoader.load_file("config/default.yml") } let(:registry) { RuboCop::Cop::Cop.registry } let(:cop_names) do registry.with_department(:Packaging).cops.map(&:cop_name) end let(:configuration_keys) { config.keys } it "has a nicely formatted description for all cops" do cop_names.each do |name| description = config[name]["Description"] expect(description.nil?).to be(false) expect(description).not_to include("\n") end end it "requires a nicely formatted `VersionAdded` metadata for all cops" do cop_names.each do |name| version = config[name]["VersionAdded"] expect(version.nil?).to(be(false), "VersionAdded is required for #{name}.") expect(version).to(match(/\A\d+\.\d+\z/), "#{version} should be format ('X.Y') for #{name}.") end end it "has a period at EOL of description" do cop_names.each do |name| description = config[name]["Description"] expect(description).to match(/\.\z/) end end it "sorts configuration keys alphabetically" do expected = configuration_keys.sort configuration_keys.each_with_index do |key, idx| expect(key).to eq expected[idx] end end it "has a SupportedStyles for all EnforcedStyle " \ "and EnforcedStyle is valid" do errors = [] cop_names.each do |name| enforced_styles = config[name] .select { |key, _| key.start_with?("Enforced") } enforced_styles.each do |style_name, style| supported_key = RuboCop::Cop::Util.to_supported_styles(style_name) valid = config[name][supported_key] unless valid errors.push("#{supported_key} is missing for #{name}") next end next if valid.include?(style) errors.push("invalid #{style_name} '#{style}' for #{name} found") end end raise errors.join("\n") unless errors.empty? end it "does not have nay duplication" do fname = File.expand_path("../../config/default.yml", __dir__) content = File.read(fname) RuboCop::YAMLDuplicationChecker.check(content, fname) do |key1, key2| raise "#{fname} has duplication of #{key1.value} " \ "on line #{key1.start_line} and line #{key2.start_line}" end end end end rubocop-packaging-0.5.1/spec/spec_helper.rb000066400000000000000000000007051374427675300207040ustar00rootroot00000000000000# frozen_string_literal: true require "rubocop-packaging" require "rubocop/rspec/support" RSpec.configure do |config| config.include RuboCop::RSpec::ExpectOffense config.disable_monkey_patching! config.raise_errors_for_deprecations! config.raise_on_warning = true config.fail_if_no_examples = true # for running specs tagged with :focus # config.filter_run_when_matching :focus config.order = :random Kernel.srand config.seed end rubocop-packaging-0.5.1/tasks/000077500000000000000000000000001374427675300162575ustar00rootroot00000000000000rubocop-packaging-0.5.1/tasks/cops_documentation.rake000066400000000000000000000016631374427675300230260ustar00rootroot00000000000000# frozen_string_literal: true require "yard" require "rubocop" require "rubocop-packaging" require "rubocop/cops_documentation_generator" YARD::Rake::YardocTask.new(:yard_for_generate_documentation) do |task| task.files = ["lib/rubocop/cop/**/*.rb"] task.options = ["--no-output"] end desc "Generate docs of all cops departments" task generate_cops_documentation: :yard_for_generate_documentation do deps = ["Packaging"] CopsDocumentationGenerator.new(departments: deps).call end desc "Verify that documentation is up to date" task verify_cops_documentation: :generate_cops_documentation do # Do not print diff and yield whether exit code was zero sh("git diff --quiet docs") do |outcome, _| exit if outcome # Output diff before raising error sh("GIT_PAGER=cat git diff docs") warn "The docs directory is out of sync. " \ "Run `rake generate_cops_documentation` and commit the results." exit! end end rubocop-packaging-0.5.1/tasks/cut_release.rake000066400000000000000000000031021374427675300214120ustar00rootroot00000000000000# frozen_string_literal: true require "bump" namespace :cut_release do %w[major minor patch pre].each do |release_type| desc "Cut a new #{release_type} release, create release notes " \ "and update documents." task release_type do run(release_type) end end def add_header_to_changelog(version) changelog = File.read("CHANGELOG.md") head, tail = changelog.split("## master (unreleased)\n\n", 2) File.open("CHANGELOG.md", "w") do |f| f << head f << "## master (unreleased)\n\n" f << "## #{version} (#{Time.now.strftime("%F")})\n\n" f << tail end end def create_release_notes(version) release_notes = new_version_changes.strip contributor_links = user_links(release_notes) File.open("relnotes/v#{version}.md", "w") do |file| file << release_notes file << "\n\n" file << contributor_links file << "\n" end end def new_version_changes changelog = File.read("CHANGELOG.md") _, _, new_changes, _older_changes = changelog.split(/^## .*$/, 4) new_changes end def user_links(text) names = text.scan(/\[@(\S+)\]\[\]/).map(&:first).uniq names.map { |name| "[@#{name}]: https://github.com/#{name}" } .join("\n") end def run(release_type) old_version = Bump::Bump.current Bump::Bump.run(release_type, commit: false, bundle: false, tag: false) new_version = Bump::Bump.current # add_header_to_changelog(new_version) # create_release_notes(new_version) puts "Changed version from #{old_version} to #{new_version}." end end