0

I'm reading about workflow/actions/CI and trying to understand how github actions work.

I have this workflow on github actions (I chose one available on Github) for Ruby on Rails.

name: "Ruby on Rails CI"
on:
  push:
    branches: ["master"]
  pull_request:
    branches: ["master"]
jobs:
  test:
    runs-on: ubuntu-latest
    services:
      postgres:
        image: postgres:11-alpine
        ports:
          - "5432:5432"
        env:
          POSTGRES_DB: rails_test
          POSTGRES_USER: rails
          POSTGRES_PASSWORD: password
    env:
      RAILS_ENV: test
      DATABASE_URL: "postgres://rails:password@localhost:5432/rails_test"
    steps:
      - name: Checkout code
        uses: actions/checkout@v3
      # Add or replace dependency steps here
      - name: Install Ruby and gems
        uses: ruby/setup-ruby@55283cc23133118229fd3f97f9336ee23a179fcf # v1.146.0
        with:
          bundler-cache: true
      # Add or replace database setup steps here
      - name: Set up database schema
        run: bin/rails db:schema:load
      # Add or replace test runners here
      - name: Run tests
        run: bin/rails test:system

  lint:
    runs-on: ubuntu-latest
    steps:
      - name: Checkout code
        uses: actions/checkout@v3
      - name: Install Ruby and gems
        uses: ruby/setup-ruby@55283cc23133118229fd3f97f9336ee23a179fcf # v1.146.0
        with:
          bundler-cache: true
      # Add or replace any other lints here
      - name: Lint Ruby files
        run: rubocop --parallel

And I'm receiving this error during the lint ruby files step.

Run rubocop --parallel
  rubocop --parallel
  shell: /usr/bin/bash -e {0}
/home/runner/work/_temp/ba9b7df9-2b25-4f55-acc3-8349cc07d754.sh: line 1: rubocop: command not found
Error: Process completed with exit code 127.

My gemfile it's updated with rubocop gems. Also already run a bundle to install.

gem 'rubocop', require: false
gem 'rubocop-rails', require: false

I did try changing in the test/lint job the line rubocop --parallel to bundle exec rubocop --parallel, but received another error related to another gem rubocop-discourse.

Everything is normal locally when I use the same commands.

After some testing, removing the line bundler-cache: true from the lint steps made it work. I don't know why, and I still want to understand why. Also, I kept only the rubocop and rubocop-rails in the install.

lint:
    runs-on: ubuntu-latest
    steps:
      - uses: actions/checkout@v3
      - uses: ruby/setup-ruby@v1
        with:
          ruby-version: 3.1.2
      - run: gem install rubocop rubocop-rails
      - name: Rubocop Lint
        run: rubocop --parallel
Rafael Moreira
  • 176
  • 1
  • 11
  • 2
    Have you seen https://stackoverflow.com/questions/65294435/github-action-fails-to-install-gem/65297382 and tried it? – riQQ Jun 16 '23 at 04:59
  • @riQQ Not exactly the post, but after reading the post I went to the docs to check something. Which got me intrigued. I understood that `bundler-cache: true` would work, but that was the problem, was not working and not caching the gems installed. I still don't know why. https://docs.github.com/en/actions/automating-builds-and-tests/building-and-testing-ruby#caching-dependencies – Rafael Moreira Jun 16 '23 at 07:28
  • If you kept the bundler cache, and the change to running rubocop via bundle's context, would adding the additional gem have fixed it? It's been a while since I looked inside the ruby setup action, but I believe if you use the bundler cache option, it will `bundle install` during the action? – Skenvy Jun 21 '23 at 12:20
  • @skenvy I tried that, still has not worked. The only way to avoid the error was to remove the Cache. I'm confused, as in the documentation, the role of the bundler-cache is just to cache the bundle. But maybe I'm missing something. – Rafael Moreira Jun 21 '23 at 23:18
  • 1
    If you run it once without the cache, and add a step before the one that fails to print out the result of `gem which rubocop`, then run it again with the modifications to add back the cache, and change the print out step to `bundle info rubocop`, what are the paths in both cases, i.e. are they the same? In both cases, also include printing out `echo $PATH | tr ':' '\n' | grep gems`. – Skenvy Jun 22 '23 at 02:44

1 Answers1

1

In the GitHub runner, the workflow;

name: "rubocop lint"
on: workflow_dispatch
jobs:
  lint:
    runs-on: ubuntu-latest
    steps:
      - name: Checkout code
        uses: actions/checkout@v3
      - name: Install Ruby and gems
        uses: ruby/setup-ruby@55283cc23133118229fd3f97f9336ee23a179fcf # v1.146.0
        with:
          ruby-version: 3.1.2
          bundler-cache: true
      - run: echo $PATH | tr ':' '\n'
      - run: bundle info rubocop
      - run: gem which rubocop
      - name: Lint Ruby files
        run: rubocop --parallel

with the Gemfile

source "https://rubygems.org"
gem 'rubocop', require: false
gem 'rubocop-rails', require: false

will show that gem binaries are added to the path at /opt/hostedtoolcache/Ruby/3.1.2/x64/bin, but that the bundle install run inside the action installed the gems to the vendor path;

/home/runner/work/<repo>/<repo>/vendor/bundle/ruby/3.1.0/gems/rubocop-1.52.1

This path can usually be configured, but it's being hard set in the action, here, to $pwd/vendor/bundle. So using the cache means you'll have to use bundle exec.

Bundler knows where these gems and their binaries are installed at, but because they are being installed in the vendor path, they are not in the system wide gem binaries /opt/hostedtoolcache/Ruby/3.1.2/x64/bin

Removing bundler-cache: true and adding the step - run: gem install rubocop rubocop-rails means the gems will be installed at the system path, i.e. their binaries are available at the ruby bin path.

In terms of understanding why one works and the other doesn't, that's why. gem install is installing to the gem home, and the action is setting the installation location to the vendor folder, which is not in the path.

If bundle exec rubocop is giving a different error, that'd be related to some other component of your project -- bundle exec would be the canonical way of invoking the executable script installed by bundler

Skenvy
  • 724
  • 1
  • 4
  • 15
  • Thanks very much for the explanation, a lot was clarified. Another question @Skenvy. During one of the jobs (TEST), the bundle cache is called as true. This cached data is used during the lint or is it exclusive to the test job? – Rafael Moreira Jun 23 '23 at 02:19
  • 1
    It should sync both jobs to read from the same cache; there are a few things that contribute to the cache key, [this being the most important aspect of it](https://github.com/ruby/setup-ruby/blob/250fcd6a742febb1123a77a841497ccaa8b9e939/bundler.js#L235-L263), along with a hash of the `Gemfile.lock`. Assuming the jobs are installing the same version of ruby, with the same `Gemfile.lock`, then the cache will match. If you don't have a lock file it'll create a temp one though, [here](https://github.com/ruby/setup-ruby/blob/250fcd6a742febb1123a77a841497ccaa8b9e939/bundler.js#L173-L179). – Skenvy Jun 23 '23 at 09:19