Github Actions + simplecov + codecov でカバレッジを表示する

rochefort/git-trend のCIは、これまで Travis-CI を利用していたのですが、 無料で使えなくなっていました(どうも2020年ぐらいからだったみたい)ので、今だとGithub Actionsが簡単に利用できそうでしたので、こちらへ移行しました。

また、カバレッジの表示もCoveralls を利用していたのですが、公開されているGithub Actions でうまく利用できなかったので、 これを機に Codecov へ移行してみました。

あんまり関係ないところで少しはまっていましたが、移行自体は簡単でした。

こんな感じ

codecov側はこんな感じです。

githubに載せるバッジはこんな感じです。

Github Actionsでrspecの実行

ruby/setup-ruby@v1 を使って rspec, rubocop を実行しています。 ここでは省略していますが、複数のrubyで実行したかったので、jobsをrubyのversion毎に作成しています。

# .github/workflows/ci.yml
name: CI

on:
  push:
  pull_request:

jobs:
  ruby32:
    runs-on: ubuntu-latest
    timeout-minutes: 10

    steps:
      - name: Checkout code
        uses: actions/checkout@v2

      - name: Setup Ruby
        uses: ruby/setup-ruby@v1
        with:
          ruby-version: '3.2.2'
          bundler-cache: true

      - name: Show ruby version
        run: ruby -v

      - name: Run tests
        run: bundle exec rspec

      - name: Run lint
        run: bundle exec rubocop

smplecov + codecov

codecov側の設定

Codecovgithubでログインし、対象リポジトリを選択すると、CODECOV_TOKEN が発行されます。

simplecov

SimpleCov::Formatter::SimpleFormatter で送れば良いので、これ用の設定は特に不要でした。

# spec/spec_helper.rb
require "simplecov"
SimpleCov.start do
  add_filter "/spec/"
end

最初は、githubに記載されてるように、ENV["CI"] でformatter分ける必要があるかと思ったのですが、なくてもlocalでhtmlとSimpleFormatterのそれぞれ出力されてるようなので、上記に変更しました。

# spec/spec_helper.rb
require "simplecov"
SimpleCov.start do
  add_filter "/spec/"

  if ENV["CI"]
    formatter SimpleCov::Formatter::SimpleFormatter
  else
    formatter SimpleCov::Formatter::MultiFormatter.new([
      SimpleCov::Formatter::SimpleFormatter,
      SimpleCov::Formatter::HTMLFormatter,
    ])
  end
end

Github Actionsの設定

これをgithubに追加します。 「Settings -> Secrets and Variables -> Actions -> Repository secrets」で追加できます。

Github Actionsワークフロー

filesとtokenオプションが必要です。 その他オプションは、Codecov · Actions · GitHub Marketplace に記載されています。

# .github/workflows/ci.yml
(略)
      - name: Upload coverage reports to Codecov
        uses: codecov/codecov-action@v3
        with:
          files: ./coverage/.resultset.json
          token: ${{ secrets.CODECOV_TOKEN }}

GithubのREADMEにつけるバッジ

codecovの設定から選択できます。

その他ハマったところ

今回合わせて、gemの更新も行ったのですが、 Simplecov.start の実行よりもアプリケーションのrequireが早く実行されており、 gemの更新により、rspecの結果がcoverageに表示されなくなってしまっていました。

これに気づかずに、codecovには送信できているが結果が何も表示されておらず、 formatterが問題ではないかと疑って、いろいろ試してしまっていました。

# before
require "simplecov"
require "webmock/rspec"
require "git_trend"   # <- これがアプリケーションコード。これより先に SimpleCov.start しないといけない
SimpleCov.start do
  add_filter "/spec/"
end
# after
require "simplecov"
SimpleCov.start do
  add_filter "/spec/"
end

require "webmock/rspec"
require "git_trend"

macOS Sonomaでのmysql2 gemのエラー対応

Sonomaでのmysql2関連のエラー対応メモです。
macOSアップグレードの度に毎回似たようなやつをググっては対応していたので、少し調べながら対応してみました。
根本的に理解するには、もっと深いところを調べる必要がありそう。
どれも、その筋の人にしか分からん対応な感じがする。

①'stdio.h' file not found

現象

bundle install時にエラーメッセージとして表示されているmkmf.logを確認します。

In file included from conftest.c:1:
In file included from /Users/rochefort/.asdf/installs/ruby/3.2.2/include/ruby-3.2.0/ruby.h:38:
In file included from /Users/rochefort/.asdf/installs/ruby/3.2.2/include/ruby-3.2.0/ruby/ruby.h:25:
/Users/rochefort/.asdf/installs/ruby/3.2.2/include/ruby-3.2.0/ruby/defines.h:16:10: fatal error: 'stdio.h' file not found
#include <stdio.h>
         ^~~~~~~~~
1 error generated.
checked program was:
/* begin */
1: #include "ruby.h"
2:
3: int main(int argc, char **argv)
4: {
5:   return !!argv[argc];
6: }
/* end */

stdio.h が無いというやつですね。近年よく見ますね。

対応

Xcode同梱のSDKを使うようにします。ググるとたくさん見つかります。

export SDKROOT="$(xcrun --sdk macosx --show-sdk-path)"

SDKROOTとは何なのか

xcrunのmanを見ると、それっぽいことが書いています。 具体的にbuild時にどうclangを呼び出してるかまでは見てないですが、ここに書かれているような実装なら SDKROOTに記載のSDKを指定することになるようです。

Specifies the default SDK to be used when looking up tools (some tools may have SDK specific versions).

This environment variable is also set by xcrun to be the absolute path to the user provided SDK 
(either via SDKROOT or the --sdk option), when it is used to invoke a normal developer tool 
(build tools like xcodebuild or make are exempt from this behavior).

          For example, if xcrun is used to invoke clang via:
             xcrun --sdk macosx clang test.c

then xcrun will provide the full path to the macosx SDK in the environment variable SDKROOT. 
That in turn will be used by clang(1) to automatically select that SDK when compiling the test.c file.

②'zstd' not found

現象

こちらもmkmf.log

(省略...)
compiling statement.c
linking shared-object mysql2/mysql2.bundle
ld: warning: ignoring duplicate libraries: '-lruby.3.2'
ld: library 'zstd' not found
clang: error: linker command failed with exit code 1 (use -v to see invocation)
make: *** [mysql2.bundle] Error 1

対応

zstdはbrewで入れてるので、環境変数を設定するのみ。これもググれば出てきます。

export LIBRARY_PATH=$(brew --prefix zstd)/lib/:$LIBRARY_PATH

LIBRARY_PATH とは何か

stackoverflowに挙がっていました。 gcc - LD_LIBRARY_PATH vs LIBRARY_PATH - Stack Overflow

When linking libraries with gcc or g++, the LIBRARY_PATH environment variable is read 

とのこと。(でもログ見ると ld なんだよなぁ。今回のは、LD_LIBRARY_PATH じゃないらしい。この辺はイマイチ分からない。)

see documentとして記載されてる、gccのマニュアルも同様のことが書かれています。 Environment Variables (Using the GNU Compiler Collection (GCC))

The value of LIBRARY_PATH is a colon-separated list of directories, much like PATH. 
When configured as a native compiler, GCC tries the directories thus specified 
when searching for special linker files, if it cannot find them using GCC_EXEC_PREFIX. 
Linking using GCC also uses these directories when searching for ordinary libraries 
for the -l option (but directories specified with -L come first).

そういえば

ググってみると、 --with-ldflags やら --with-opt-dir やらの話も出てきて、以下の対応入れるやつですが 今回はopenssl@3を入れてLDFLAGSあたりを設定してるから?か対応不要でした。(よく分からん)

$ bundle config --global build.mysql2 "--with-opt-dir=$(brew --prefix openssl)"

See Also

macOS Sonoma 14.1.2にrubyをinstallする - rochefort's blog

macOS Sonoma 14.1.2にrubyをinstallする

会社用のM3 Macが届いたので、さっそくrubyをinstallしようとしたら、エラーでハマったのでメモしておく。

エラー内容

$ RUBY_CONFIGURE_OPTS="--with-openssl-dir=$(brew --prefix openssl)" asdf install ruby 3.2.2
To follow progress, use 'tail -f /var/folders/nb/0lr36cbj6gx01xsgwxmwc6jh0000gn/T/ruby-build.20231203194651.23142.log' or pass --verbose
Downloading ruby-3.2.2.tar.gz...
-> https://cache.ruby-lang.org/pub/ruby/3.2/ruby-3.2.2.tar.gz
Installing ruby-3.2.2...
ruby-build: using readline from homebrew
ruby-build: using libyaml from homebrew
ruby-build: using gmp from homebrew

BUILD FAILED (macOS 14.1.2 using ruby-build 20230424)

Inspect or clean up the working tree at /var/folders/nb/0lr36cbj6gx01xsgwxmwc6jh0000gn/T/ruby-build.20231203194651.23142.RhNS49
Results logged to /var/folders/nb/0lr36cbj6gx01xsgwxmwc6jh0000gn/T/ruby-build.20231203194651.23142.log

Last 10 log lines:
checking for llvm-nm... llvm-nm
checking for llvm-objcopy... no
checking for llvm-objdump... llvm-objdump
checking for llvm-ranlib... no
checking for llvm-strip... no
checking for gcc... clang
checking whether the C compiler works... no
configure: error: in `/var/folders/nb/0lr36cbj6gx01xsgwxmwc6jh0000gn/T/ruby-build.20231203194651.23142.RhNS49/ruby-3.2.2':
configure: error: C compiler cannot create executables
See `config.log' for more details

ぱっと見

よく分からない。config.log も見てみるがこちらもイマイチ分からない。 ただ、llvm系の何かが足りておらず、Cコンパイラがうまく起動してないことだけは分かる。

MacのCコンパイラは Clang と呼ばれるもので、こいつはXCodeのCommandLineToolsに同梱されている。 とりあえずversionを見てみると、、、

$ clang --version
Apple clang version 15.0.0 (clang-1500.0.40.1)
Target: arm64-apple-darwin23.1.0
Thread model: posix
InstalledDir: /Library/Developer/CommandLineTools/usr/bin

binの場所も表示されている。llvm系を確認すると、確かにinstall時のログに出ていたものが存在しないようだ。

$ ll /Library/Developer/CommandLineTools/usr/bin | grep llvm
lrwxr-xr-x  1 root  wheel    12B Dec  3 05:18 c++filt@ -> llvm-cxxfilt
lrwxr-xr-x  1 root  wheel    14B Dec  3 05:18 dwarfdump@ -> llvm-dwarfdump
lrwxr-xr-x  1 root  wheel     8B Dec  3 05:18 gcov@ -> llvm-cov
-rwxr-xr-x  1 root  wheel    12M Sep  7 07:24 llvm-cov*
-rwxr-xr-x  1 root  wheel   1.1M Sep  7 07:23 llvm-cxxfilt*
-rwxr-xr-x  1 root  wheel    23M Sep  7 07:24 llvm-dwarfdump*
-rwxr-xr-x  1 root  wheel    28M Sep  7 07:23 llvm-nm*
-rwxr-xr-x  1 root  wheel    31M Sep  7 07:24 llvm-objdump*
-rwxr-xr-x  1 root  wheel   166K Sep  7 07:24 llvm-otool*
-rwxr-xr-x  1 root  wheel    13M Sep  7 07:23 llvm-profdata*
-rwxr-xr-x  1 root  wheel    10M Sep  7 07:23 llvm-size*
lrwxr-xr-x  1 root  wheel     7B Dec  3 05:18 nm@ -> llvm-nm
lrwxr-xr-x  1 root  wheel    12B Dec  3 05:18 objdump@ -> llvm-objdump
lrwxr-xr-x  1 root  wheel    10B Dec  3 05:18 otool@ -> llvm-otool

とりあえずbinaryでversionを確認してみると、LLVMは version 15系ということがわかった。

$ llvm-cov --version
Apple LLVM version 15.0.0
  Optimized build.
  Default target: arm64-apple-darwin23.1.0
  Host CPU: apple-m1

homebrewでllvmを入れる

brewにあるだろうと思って、調べてみると、現在は17系のようだ。

$ brew info llvm
==> llvm: stable 17.0.6 (bottled), HEAD [keg-only]

とりあえず入れてみる。

$ brew install llvm

以下にbinがあったので確認してみる。たくさんあるので省略しますが、install時に「no」と表示されていたものがある。

$ ll /opt/homebrew/opt/llvm/bin | grep llvm
-r-xr-xr-x  1 rochefort  admin    37K Nov 28 17:52 llvm-PerfectShuffle*
lrwxr-xr-x  1 rochefort  admin    15B Nov 28 17:52 llvm-addr2line@ -> llvm-symbolizer
-rwxr-xr-x  1 rochefort  admin   108K Nov 28 17:52 llvm-ar*
....(略)

気を取り直してinstall

llvm install時とbrew info 時に以下のメッセージが表示されているので、それぞれ設定する。 (なぜかbinの方のメッセージはinstall時には出ていなかった)

If you need to have llvm first in your PATH, run:
  echo 'export PATH="/opt/homebrew/opt/llvm/bin:$PATH"' >> ~/.zshrc

For compilers to find llvm you may need to set:
  export LDFLAGS="-L/opt/homebrew/opt/llvm/lib"
  export CPPFLAGS="-I/opt/homebrew/opt/llvm/include"

あとは、いつものようにinstallするだけ。

$ RUBY_CONFIGURE_OPTS="--with-openssl-dir=$(brew --prefix openssl)" asdf install ruby 3.2.2

最初llvm/clang周りは CommandLineTools を最新にしたり、beta版を使ったり、ダウングレードしたりすれば直る可能性があるので いろいろ試していたが、どれもダメだった。


追記

Operation not permitted @ dir_s_mkdir

現象

古いrubyだとこのエラーが出る。

/private/var/folders/nb/0lr36cbj6gx01xsgwxmwc6jh0000gn/T/ruby-build.20231204201622.35404.ClJqe6/ruby-3.1.3/lib/fileutils.rb:240:in `mkdir': Operation not permitted @ dir_s_mkdir - /Applications/Xcode.app/Contents/Developer/Platforms/MacOSX.platform/Developer/SDKs/MacOSX14.0.sdk/Users (Errno::EPERM)

対応

macPrivacy & Security 設定にterminalからchownできるようにしておきます。

あとは該当ディレクトリをchown

$ sudo chown $(whoami) /Applications/Xcode.app/Contents/Developer/Platforms/MacOSX.platform/Developer/SDKs/MacOSX14.0.sdk

参考