Linter and GitHub actions and Rebasing

Dejan Vujovic
8 min readMay 11, 2023

--

This is the third part of the tutorial. In the first part, we created a code for logging in via Facebook and a passwordless login with a phone number and verification code. In the second part, we dealt with best practices for Ruby on Rails and refactored the code. In this part, I will explain best practices for linter and GitHub actions.

If you haven’t followed the first two parts, here are the links, part one, and part two. If you don’t already have one, clone the GitHub repo here. From the develop branch, create a new branch ‘linter-github-actions’.

Before delving into what linting is, it’s important to understand the base term lint. In programming, lint refers to code that is either suboptimal, ugly, or may have a bug despite being technically okay. On the other hand, a linter is a dedicated tool for detecting lint in your code. In this tutorial, we will install Rubocop, Stylelint, and Eslint for Tailwind CSS. Also, we will create GitHub actions to check errors on pull requests for linter and tests.

Let’s start with Rubocop. RuboCop is a Ruby code-style checker (linter). RuboCop is extremely flexible and most aspects of its behavior can be tweaked via various configuration options. In practice, RuboCop supports pretty much every (reasonably popular) coding style that you can think of. Apart from reporting problems in your code, RuboCop can also automatically fix some of the problems for you.

We will not install Rubocop, but gem standard. These days many developers prefer gem standard. The standard gem brings the ethos of StandardJS to Ruby. It's a linter & formatter built on RuboCop and provides an unconfigurable configuration to all of RuboCop's built-in rules as well as those included in robocop-performance. It also supports plugins built with lint_roller, like standard-rails. Add this line to the Gemfile and run bundle install:

gem 'standard'

Next, add this to your Rakefile:

require "standard/rake"

Now run ‘rake standard’.

It will give you errors in the code. Awesome. Just run ‘rake standard:fix’ to fix errors automatically. We have one error we can't fix. Great:

standard: Use Ruby Standard Style (https://github.com/standardrb/standard)
app/jobs/send_pin_job.rb:6:5: Lint/UselessAssignment: Useless assignment to variable - `resp`.
rake aborted!

It says we need to remove useless code. Amazing. Let’s just update jobs/send_pin_job.rb file:

class SendPinJob < ActiveJob::Base
def perform(user)
nexmo = Nexmo::Client.new(api_key: "your api_key", api_secret: "your api_secret")
nexmo.sms.send(from: "Ruby", to: user.phone, text: user.pin.to_s)
user.touch(:pin_sent_at)
end
end

Run rake standard and no more errors.

Now I will show you how to install a linter for SCSS or CSS 3. Stylelint. We won’t be using it now because we’re using Tailwind CSS. Certainly, if you use CSS and preprocessors you should have this both locally and in GitHub shares. Run:

npm install --save-dev stylelint@13.x stylelint-scss@3.x stylelint-config-standard@21.x stylelint-csstree-validator@1.x

Create .stylelintrc.json to the root directory of your project.It should look like this:

{
"extends": ["stylelint-config-standard"],
"plugins": ["stylelint-scss", "stylelint-csstree-validator"],
"rules": {
"at-rule-no-unknown": [
true,
{
"ignoreAtRules": [
"tailwind",
"apply",
"variants",
"responsive",
"screen"
]
}
],
"scss/at-rule-no-unknown": [
true,
{
"ignoreAtRules": [
"tailwind",
"apply",
"variants",
"responsive",
"screen"
]
}
],
"csstree/validator": true
},
"ignoreFiles": ["build/**", "dist/**", "**/reset*.css", "**/bootstrap*.css"]
}
  1. Run npx stylelint "**/*.{css,scss}" on the root of the directory of your project to see errors.
  2. Fix linter errors with npx stylelint "**/*.{css,scss}" --fix.

This was just an example, don’t install it in the code, because we will install Eslint for Tailwind CSS now. Run:

npm i eslint --save-dev

And:

npm i eslint-plugin-tailwindcss --save-dev

Now things get complicated because we need the linter to check our erb files. Let’s fix that too. We will create a .eslintrc file in the root of the application and override eslint behavior. We need a parser to make it. I will use an angular parser because there is no one for rails. Run in your terminal:

npm i @angular-eslint/template-parser

The best practice is to move it from dependencies to devDepedencies in your package.json file:

{
"devDependencies": {
"eslint": "^8.40.0",
"eslint-plugin-tailwindcss": "^3.11.0",
"@angular-eslint/template-parser": "^16.0.1"
}

Next, create in the root of your application .eslintrc file which should look like this:

{
"plugins": [
"tailwindcss"
],
"extends": [
"plugin:tailwindcss/recommended"
],
"overrides": [
{
"files": [
"app/**/*.erb"
],
"parser": "@angular-eslint/template-parser"
}
],
"rules": {
"tailwindcss/classnames-order": "error",
"tailwindcss/no-custom-classname": "warn",
"tailwindcss/no-contradicting-classname": "error"
}
}

As we see we can change here if we want the linter to show errors or warnings. Let’s back to add some scripts in the package.json file:

{
"scripts": {
"lint": "eslint app --ext .erb",
"lint:fix": "eslint ./app/**/*.erb --fix"

},
"devDependencies": {
"eslint": "^8.38.0",
"eslint-plugin-tailwindcss": "^3.11.0",
"@angular-eslint/template-parser": "^15.2.1"
}
}

Now we can run ‘npm run lint’ to see Tailwind CSS errors, and ‘npm run lint:fix’ to fix errors. Amazing. It was not easy. Just use this awesome code for your Ruby on Rails application with Tailwind CSS code to fix errors. It can give you errors or warnings, depending on how you set it in the .eslintrc file:

Now just we need to add testing and check tests in GitHub actions. In this application, we didn't test the behavior of the code, but we will use it here RSpec, just to show you installation and GitHub actions. I prefer RSpec and many developers use it. Just delete folder tests as we don't need them. Somebody will say I should test the code before, but I will leave it to you for practice. I recommend doing fast request tests with RSpec, and before testing please try to have refactored code and respect the Single-responsibility principle for your classes. Let’s install RSpec fast. In Gemfile add:

group :development, :test do
# See https://guides.rubyonrails.org/debugging_rails_applications.html#debugging-with-the-debug-gem
gem "rspec-rails"
gem "factory_bot_rails"
gem "faker"
end

Run:

bundle install
rails g rspec:install

Create the ‘support’ directory in the ‘spec’ directory and create two files: spec/support/factory_bot.rb and spec/support/chrome.rb

Configure FactoryBot:

# spec/support/factory_bot.rb

RSpec.configure do |config|
config.include FactoryBot::Syntax::Methods
end

Configure driver:

# spec/support/chrome.rb

RSpec.configure do |config|
config.before(:each, type: :system) do
if ENV["SHOW_BROWSER"] == "true"
driven_by :selenium_chrome
else
driven_by :selenium, using: :headless_chrome, screen_size: [1400, 1400]
end
end
end

Require support files in rails_helper.rb:

# spec/rails_helper.rb

require_relative 'support/factory_bot'
require_relative 'support/chrome'

As we do not have any test it will be all green, 0 errors:

Finished in 0.00251 seconds (files took 0.27592 seconds to load)
0 examples, 0 failures

Fine. Now we can create GitHub actions. We will do it for gem standard and RSpec. Tailwind errors we can fix locally, and as I said before we don't need Stylelint.

Create a new folder at the root of the application ‘.github’ and subfolder ‘workflows’. In the ‘workflows’ folder create a file linters.yml:

name: Actions

on: pull_request

jobs:
standard:
name: Standard
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v3
- uses: ruby/setup-ruby@v1
with:
bundler-cache: true
- name: Set up Node
uses: actions/setup-node@v3
with:
node-version: 18
- name: Setup Standard
run: |
sudo apt-get -yqq install libpq-dev
gem install bundler
bundle install --jobs 4 --retry 3
npm install
- name: Standard Report
run: bundle exec standardrb
eslint:
name: Tailwind CSS
runs-on: ubuntu-22.04
steps:
- uses: actions/checkout@v3
- uses: actions/setup-node@v3
with:
node-version: "18.x"
- name: Setup Tailwnd CSS
run: |
npm install --save-dev eslint eslint-plugin-tailwindcss @angular-eslint/template-parser
[ -f .eslintrc ]
- name: Tailwind CSS Report
run: npm run lint .
nodechecker:
name: node_modules checker
runs-on: ubuntu-22.04
steps:
- uses: actions/checkout@v2
- name: Check node_modules existence
run: |
if [ -d "node_modules/" ]; then echo -e "\e[1;31mThe node_modules/ folder was pushed to the repo. Please remove it from the GitHub repository and try again."; echo -e "\e[1;32mYou can set up a .gitignore file with this folder included on it to prevent this from happening in the future." && exit 1; fi
test:
name: RSpec
runs-on: ubuntu-22.04
services:
postgres:
image: postgres:14-alpine
ports:
- "5432:5432"
env:
POSTGRES_DB: kindergarten_exchange_test
POSTGRES_USER: rails
POSTGRES_PASSWORD: password
options: >-
--health-cmd pg_isready
--health-interval 10s
--health-timeout 5s
--health-retries 5
env:
RAILS_ENV: test
DATABASE_URL: "postgres://rails:password@localhost:5432/starter_rails_api_test"
steps:
- name: Checkout code
uses: actions/checkout@v3
# Add or replace dependency steps here
- name: Install Ruby and gems
uses: ruby/setup-ruby@v1
with:
bundler: default
bundler-cache: true
# Add or replace database setup steps here
- name: Create database tables
run: bin/rails db:create
- name: Set up database schema
run: bin/rails db:schema:load
# Add or replace test runners here
- name: Run tests
run: bin/bundle exec rspec spec

It is working. Just be sure you have stored credentials in Rails encrypted file or the Rails database will throw an error.

For now, we have this after the pull request in GitHub:

If you have any errors in code or tests can't pass it will show errors in GitHub actions.

One more thing. I made many commits to fixing errors. It looks ugly:

When we work with the team, it can be confusing. Or if somebody read our code in GitHub. There is a way to fix it. Rebasing. From a content perspective, rebasing is changing the base of your branch from one commit to another making it appear as if you’d created your branch from a different commit. Internally, Git accomplishes this by creating new commits and applying them to the specified base. It’s very important to understand that even though the branch looks the same, it’s composed of entirely new commits.

In this branch ‘linter-github-actions’ we have 10 commits. Let’s rebase commits for this branch so we have just one commit:

git rebase -i HEAD~10

Git opens the last five commits in your terminal text editor, oldest commit first. Each commit shows the action to take on it, the SHA, and the commit title:

After the list of commits, a commented-out section shows some common actions you can take on a commit:

  • Pick a commit to use it with no changes. The default option.
  • Reword a commit message.
  • Edit a commit to use it, but pause the rebase to amend (add changes to) it.
  • Squash multiple commits together to simplify the commit history of your feature branch.

Replace the keyword pick according to the operation you want to perform in each commit. To do so, edit the commits in your terminal’s text editor.

We will replace ‘pick’ with ‘reword’ for the first commit and for other commits with ‘squash’. Depending on what text editor you have in your terminal, just edit text, save text, and exit from a text editor. In terminal run:

git push --force

We have this now in GitHub:

Just one commit, without necessary confusing and redundant commits.

Awesome!!! Thank you for reading this tutorial, and see you soon with some new ideas.

--

--