Linter for Enforcing Downstream Checks
Major Project Presentation
Utkarsh Gupta
Samyak Jain
(8CSE - 8Y)
Presented By:
(A2305217557)
(A2305217638)
ASET (B.Tech. CSE)
Faculty Guide:
Dr. Pooja Singh
Assistant Professor
(Deptartment of CSE)
ASET, AUUP
About the project
The main goal of this project is to provide a tool to automatically detect those issues and report them upstream.
WHAT IS THE PROJECT? AND WHAT ARE THE GOALS?
During the maintenance of the Ruby packages in Debian, several issues in upstream codebases have been identified that make it difficult to build a Debian package out of Ruby gems.
What is Debian?
Debian is a free operating system (OS) for your computer. An operating system is the set of basic programs and utilities that make your computer run.
Debian provides more than a pure OS: it comes with over 65000 packages, precompiled software bundled up in a nice format for easy installation on your machine.
- For the problem being faced, the best solution is to write a linter in the Ruby language.
WHAT IS TO BE DONE?
SOLUTION
-
To write a linter (for any language),
the first thing would be to create
an Abstract Syntax Tree.
- For a linter to work, the whole source code needs to be traversed and processed in the form of an AST.
- On that AST, we can write and match node patterns to match the problematic lines and report them directly.
IMPLEMENTATION
HOW IS IT TO BE DONE?
class GemspecGit < Cop
def_node_search :xstr, <<~PATTERN
(block
(send
(const
(const {cbase nil?} :Gem) :Specification) :new)
(args
(arg _)) `$(xstr (str start_with('git'))))
PATTERN
The usage of `git ls-files` in the `gemspec` file can be determined by the following AST:
STEP 1:
IMPLEMENTATION
HOW IS IT TO BE DONE?
def investigate(processed_source)
xstr(processed_source.ast).each do |node|
add_offense(
processed_source.ast,
location: node.loc.expression,
message: MSG
)
end
end
The next thing is to process the AST formed against the source code to match problematic lines:
STEP 2:
IMPLEMENTATION
HOW IS IT TO BE DONE?
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
end
Write tests \o/
STEP 3:
IMPLEMENTATION
HOW IS IT TO BE DONE?
class RequireRelativeHardcodingLib < Base
include RuboCop::Packaging::LibHelperModule
extend AutoCorrector
def_node_matcher :require_relative, <<~PATTERN
(send nil? :require_relative
(str #falls_in_lib?))
PATTERN
The usage of `require_relative` in the spec/ dir can be determined by the following AST:
STEP 1:
IMPLEMENTATION
HOW IS IT TO BE DONE?
def on_new_investigation
@file_path = processed_source.file_path
@file_directory = File.dirname(@file_path)
end
def on_send(node)
return unless require_relative(node)
add_offense(node) do |corrector|
corrector.replace(node, good_require_call)
end
end
The next thing is to process the AST formed against the source code to match problematic lines:
STEP 2:
IMPLEMENTATION
HOW IS IT TO BE DONE?
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
end
end
end
Write tests \o/
STEP 3:
USAGE
HOW TO USE THIS TOOL?
Now, the tool is ready to be deployed, let's use this in other projects:
(this correctly determies the usage of `git ls-files` in the `gemspec` file)
END PRODUCT
WHAT IS THE END OUTCOME
OF THIS PROJECT?
I expect the following outcome from this entire project:
- The tool should be able to, at least, correctly determine around 5-8 major problems and report it.
- The tool will be deployed to production and will be available to all to install via `apt install` or `gem install`.
- It should be helping Debian (downstream!) and other upstream libraries should be using it on the CI.
Where's the tool used?
Selected for Google Summer of Code - 2020
(https://wiki.debian.org/SummerOfCode2020/Projects)
(https://wiki.debian.org/SummerOfCode2020/Projects)
Being used by hundreds of people, already.
(https://github.com/utkarsh2102/rubocop-packaging)
THANK YOU!
RuboCop :: Packaging
By utkarsh2102
RuboCop :: Packaging
This slide is made for the purpose of my major and minor projects.
- 641