DEV Community

Cover image for 用Github Actions 持續整合 Ruby on Rails
Kevin Luo
Kevin Luo

Posted on

4 3

用Github Actions 持續整合 Ruby on Rails

最近研究了一下用 Github Actions 做 Rails 的 CI ,分享一下經驗 : )

Github Actions 是 Github 的自動化工具。
Github Actions 只要在你的專案根目錄新增 .github/workflows ,再新增任意名稱的 Yaml 檔。
Git push 到 Github 後,即會根據你寫的 workflow 的內容自動執行了。

可以直接看下面分享的 YAML 檔,不過建議還是先看一下 Github Action 的文檔 Introduction to GitHub Actions - GitHub Docs ,會比較有概念喔(常常更新的也滿快的)

先分享 .github/workflows/ci.yml,後面會再說明每一列是代表什麼意思



name: CI
on: [push, pull_request]
jobs:
  build:
    runs-on: ubuntu-latest
    steps:
      - uses: actions/checkout@v1
      - name: Setup Ruby
        uses: ruby/setup-ruby@v1
        with:
          ruby-version: 2.7.2
          bundler-cache: true
      - name: Install Node
        uses: actions/setup-node@v2
        with:
          node-version: '12.16.x'
      - name: Restore cached ./node_modules
        uses: actions/cache@v2
        with:
          path: ./node_modules
          key: ${{ runner.os }}-yarn-lock-${{ hashFiles('./yarn.lock') }}
          restore-keys: |
            ${{ runner.os }}-yarn-lock-
      - name: Yarn Install
        run: yarn install
  test:
    needs: build
    runs-on: ubuntu-latest
    services:
      mysql:
        image: mysql:8
        ports: ['3306:3306']
        env:
          MYSQL_ROOT_PASSWORD: 'my-root-pw'
          MYSQL_DATABASE: test_db
          MYSQL_USER: username_you_like
          MYSQL_PASSWORD: password_you_like
        options: --health-cmd="mysqladmin ping" --health-interval=10s --health-timeout=5s --health-retries=3
      redis:
        image: redis
        ports: ['6379:6379']
        options: --entrypoint redis-server
    steps:
      - uses: actions/checkout@v1
      - name: Setup Ruby
        uses: ruby/setup-ruby@v1
        with:
          ruby-version: 2.7.2
          bundler-cache: true
      - name: Install Node
        uses: actions/setup-node@v2
        with:
          node-version: '12.16.x'
      - name: Restore cached ./node_modules
        uses: actions/cache@v2
        with:
          path: ./node_modules
          key: ${{ runner.os }}-yarn-lock-${{ hashFiles('./yarn.lock') }}
          restore-keys: |
            ${{ runner.os }}-yarn-lock-
      - name: Yarn Install
        run: yarn install
      - name: Prepare Database
        env:
          RAILS_ENV: test
          RAILS_MASTER_KEY: ${{ secrets.RAILS_MASTER_KEY }}
        run: bundle exec rails db:prepare
      - name: Run tests
        env:
          REDIS_URL: redis://localhost:6379/1
          RAILS_ENV: test
          RAILS_MASTER_KEY: ${{ secrets.RAILS_MASTER_KEY }}
        run: |
          bundle exec rspec --format RspecJunitFormatter --out ./reports/rspec.xml
      - name: Publish Test Report
        uses: mikepenz/action-junit-report@v2
        with:
          report_paths: './reports/rspec.xml'


Enter fullscreen mode Exit fullscreen mode

說明版



# 此 workflow 的名稱,可任意取,到時會出現在 Github Actions 中
name: CI
# on 是控制何時要執行這個 workflow
# * push: 有 commit  push 時
# * pull_request: PR 更新時
on: [push, pull_request]
# 一個 workflow 可以有很多jobs 組成,它們通常是同時(平行)執行的,
#不過可以設定先後順序,下面一點會提到。
#job 的名稱可任意取名,我這裡叫它 build
# `runs-on` 是 job 要在什麼 OS 上執行, github 上的 Github Actions 好像得用 ubuntu-latest,我也是照教學沿用了
jobs:
  build: 
    runs-on: ubuntu-latest 
    # `step` 是 Github Actions 的最小單位
    # steps 裡每個 step 可以執行一或多個指令或一個 Action
    # 要執行指令,則使用 `run`
    # `run: echo "hello world!"`
    # 要執行 Action,用 `uses`
    # `uses: actions的名字`
    # 每個 step 的 name 並不是必填,但填了就像註解一樣,方便理解
    steps:
      # Action 其實就預先寫好的腳本
      # GitHub Marketplace 上有一堆,可以想像是想把自動化的腳本當成在 App Store 上賣
      # [GitHub Marketplace · Actions to improve your workflow · GitHub](https://github.com/marketplace?type=actions)
      #`actions/checkout@v1` 就是一個常用的 Action,可以把你的 git repository 下載下來
      - uses: actions/checkout@v1

      # ruby/setup-ruby@v1 是用來安裝 Ruby 的 Action
      # 下載的 ruby-version: 2.7.2 是要裝 2.7.2 的 Ruby
      # bundler-cache: true 是要快取 bundler 下載的 gem,下面再說明快取
      # 基本上這些 Action 都可以在 Github Marketplace 上查到用法
      # [Setup Ruby, JRuby and TruffleRuby · Actions · GitHub Marketplace · GitHub](https://github.com/marketplace/actions/setup-ruby-jruby-and-truffleruby)
      - name: Setup Ruby
        uses: ruby/setup-ruby@v1
        with:
          ruby-version: 2.7.2
          bundler-cache: true
      # 同上,安裝 Node
      - name: Install Node
        uses: actions/setup-node@v2
        with:
          node-version: '12.16.x'
      # `actions/cache@v2` 是專門來做快取的 Action
      # 這裡寫 path: ./node_modules 就是要把 ./node_modules 下的檔案全 cache 起來
      # 快取的結果會用 key 的設定去命名。
      # 這個 action 會先依照 restore-keys 去找可以回復的快取
      # 最後的 step 純粹就是跑 yarn install

      # 如果光看這個 順序應該有人會疑惑:
      # 快取的 action 是放在 yarn install 前,或更上方 ruby 的部分有 bundle install 前,
      # 在安裝依賴前,先「讀取」快取好理解,
      # 但重要的是「存」快取的時機怎麼沒看到?
      # 執行一次就知道,這類快取的 Action 都有掛個動作在 PostJob 的 callback,
      # 當這個 job 結束後會把該快取的 path 存起來

      # 另外,每個倉庫有 5GB 的快取空間,正常來說應該是用不完啦。
      # 所以 gems, npm 這種安裝的程式庫都把它們快取起來吧
      - name: Restore cached ./node_modules
        uses: actions/cache@v2
        with:
          path: ./node_modules
          key: ${{ runner.os }}-yarn-lock-${{ hashFiles('./yarn.lock') }}
          restore-keys: |
            ${{ runner.os }}-yarn-lock-
      - name: Yarn Install
        run: yarn install
  # 這是第二個 job ,我命名為 test,因為準備要跑 rspec 了
  # 上面說 job 其實是平行執行的,`needs` 可以控制先後順序,needs: build 的意思就是 build 跑完才會跑 test
  # 雖然以目前這個 workflow 來看,其實可以把steps 全寫在同一個 job ,沒什麼差別。
  # 但寫成這樣,可以方便未來加入不同類的 test,比如 js 的 test, capybara 的 test,可以在 build 後,所有的 test 同時執行。
  test:
    needs: build
    runs-on: ubuntu-latest
    # `services` 可以用 docker image 架起需要用的服務,設定好 port  mapping
    # 我這裡架了 mysql 和 redis
    # 那些 env 是去 DockerHub 上去找官方的 image 的說明才知道有什麼可以加的
    # 順帶一提,CI 時 `config/database.yml` 可以兩種處理方式
    # * 直接加入 git,內容再用 ENV 去替換
    # * 新增一個 database.yml.ci ,在 workflow 裡加一個 step 去 `run: cp config/database.yml.ci config/database.yml`
    services:
      mysql:
        image: mysql:8
        ports: ['3306:3306']
        env:
          MYSQL_ROOT_PASSWORD: 'my-root-pw'
          MYSQL_DATABASE: test_db
          MYSQL_USER: test_user
          MYSQL_PASSWORD: test_pw
        options: --health-cmd="mysqladmin ping" --health-interval=10s --health-timeout=5s --health-retries=3
      redis:
        image: redis
        ports: ['6379:6379']
        options: --entrypoint redis-server
    # 下面這段到 yarn install 跟 build 是做完全一模一樣的事,執行時會直接取 build 快取的結果,所以很快就會跑完。
    # 看起來很冗,不過如果未來可以用 YAML 的 Anchor 功能,這一塊可以直接寫成一個可複用的 block,
    # 目前就直接重複吧!
    steps:
      - uses: actions/checkout@v1
      - name: Setup Ruby
        uses: ruby/setup-ruby@v1
        with:
          ruby-version: 2.7.2
          bundler-cache: true
      - name: Install Node
        uses: actions/setup-node@v2
        with:
          node-version: '12.16.x'
      - name: Restore cached ./node_modules
        uses: actions/cache@v2
        with:
          path: ./node_modules
          key: ${{ runner.os }}-yarn-lock-${{ hashFiles('./yarn.lock') }}
          restore-keys: |
            ${{ runner.os }}-yarn-lock-
      - name: Yarn Install
        run: yarn install
      # 如果要開始跑 rails 的指令時,要帶一些`環境變數`時,例如我這樣會用 RAILS_ENV 跟 RAILS_MASTER_KEY
      # 是密碼類的字串可以存在Github倉庫 >Settings>Secrets 裡,workflow 裡可以直接 `${{ secrets.RAILS_MASTER_KEY }}` 去讀取
      - name: Prepare Database
        env:
          RAILS_ENV: test
          RAILS_MASTER_KEY: ${{ secrets.RAILS_MASTER_KEY }}
        run: bundle exec rails db:prepare
      # 這邊是跑 rspec
      # 有裝 rspec_junit-formatter ,所以可以產生一個 XML 檔紀錄測試結果
      - name: Run tests
        env:
          REDIS_URL: redis://localhost:6379/1
          RAILS_ENV: test
          RAILS_MASTER_KEY: ${{ secrets.RAILS_MASTER_KEY }}
        run: bundle exec rspec --format RspecJunitFormatter --out ./reports/rspec.xml
      - name: Publish Test Report
        uses: mikepenz/action-junit-report@v2
        with:
          report_paths: './reports/rspec.xml'


Enter fullscreen mode Exit fullscreen mode

最後用 mikepenz/action-junit-report@v2
去讀取 XML 的測試結果並秀出在頁面上
junit xml


心得

我覺得自動化最煩的就是一堆小細節要顧,
比如說也可以完全不快取硬讓它跑,但就會很慢。
像 CircleCI 的 orb 就是把每個語言、框架常用的自動化指令整理起來變成一組指令集。
但 Github Actions 直接更進一步讓社群製作指令集,並準備好市集,方便搜尋跟分享,野心勃勃要幹掉其它 CI。

整體上還滿容易用,連 Rails 都能輕易導入(回想一下把 Rails 裝進 Docker 的夜晚),滿推薦試試看的。
而且Github(微軟)滿佛的,每個月給2000小時,個人練習或小型專案應該是用不完啦,可以去帳號 Billing & Plans 下查用量。
alt text

參考資料

Heroku

This site is built on Heroku

Join the ranks of developers at Salesforce, Airbase, DEV, and more who deploy their mission critical applications on Heroku. Sign up today and launch your first app!

Get Started

Top comments (0)

Sentry image

See why 4M developers consider Sentry, “not bad.”

Fixing code doesn’t have to be the worst part of your day. Learn how Sentry can help.

Learn more

👋 Kindness is contagious

Please leave a ❤️ or a friendly comment on this post if you found it helpful!

Okay