GitHub Actionsで#<Errno::ENOTTY

発生した経緯

自作のRubyGemのテストとCIを整えていた時に遭遇。 パスワード入力の所でIO#noechoを使うように修正した所、GitHub Actions内で以下のようなエラーが起きた。

   1) GreenDay::Cli login valid name and password create cookie-store
     Failure/Error: password = STDIN.noecho { |stdin| stdin.gets(chomp: true) }.tap { puts }

     Errno::ENOTTY:
       Inappropriate ioctl for device
     # ./lib/green_day/cli.rb:19:in `noecho'
     # ./lib/green_day/cli.rb:19:in `login'
     # ./spec/cli_spec.rb:83:in `block (3 levels) in <top (required)>'
     # ./spec/cli_spec.rb:101:in `block (4 levels) in <top (required)>' 

ローカルでは通ったのでCI環境での問題っぽい。

解決まで

"GitHub Actions Inappropriate ioctl for device"でググった所以下のissueを発見。

github.com

どうやらTTYなるもので無いのが駄目らしい。

ワークアラウンドらしいけどこのコメントで解決した

https://github.com/actions/runner/issues/241#issuecomment-577360161

scriptはデフォルトでtypescriptってファイルにターミナルのsessionをまるごと記録してくれるもの。 仕組みはよくわかってないけど実際にローカルでscriptを叩くとセッションが新しく始まって後述のttyコマンドの結果も変化したのでなにかしらttyに当たるものを作るとうまく行く原理なのだろうか。

travis ciでは問題が再現しなかったのでGitHub Actions特有の問題っぽい。Dockerコンテナの出力をキャプチャしているのが関係しているのかもしれない。

最終的にscript -e -cをつけてテストを実行するとエラーは起きなくなった。 https://github.com/QWYNG/green_day/blob/1e174b17201743723a98e7ae245de4fe27e4bbc6/.github/workflows/ruby.yml#L28

tty is 何

最後にttyというワードも初めて知ったのでメモ。 tty自体はUNIXコマンド

 man tty

TTY(1)                                                   User Commands                                                   TTY(1)

NAME
       tty - print the file name of the terminal connected to standard input 

ターミナルに入力していると思っていた標準入力は実際はこのファイルへ送っているらしい。

  ~  ps                                              2020年07月16日 23時00分57秒
    PID TTY          TIME CMD
  83617 pts/2    00:00:00 fish
  83739 pts/2    00:00:00 ps
 ~  ll /proc/83617/fd                               2020年07月16日 23時01分00秒
合計 0
lrwx------ 1 qwyng qwyng 64  7月 16 23:00 0 -> /dev/pts/2
lrwx------ 1 qwyng qwyng 64  7月 16 23:00 1 -> /dev/pts/2
lrwx------ 1 qwyng qwyng 64  7月 16 23:00 2 -> /dev/pts/2
lr-x------ 1 qwyng qwyng 64  7月 16 23:00 3 -> /home/qwyng/
lr-x------ 1 qwyng qwyng 64  7月 16 23:00 4 -> 'pipe:[727942]'
l-wx------ 1 qwyng qwyng 64  7月 16 23:00 5 -> 'pipe:[727942]'
lr-x------ 1 qwyng qwyng 64  7月 16 23:00 6 -> 'pipe:[728533]'
l-wx------ 1 qwyng qwyng 64  7月 16 23:00 7 -> 'pipe:[728533]'
lrwx------ 1 qwyng qwyng 64  7月 16 23:00 8 -> /run/user/1000/fish_universal_variables.notifier|
 ~  tty                                             2020年07月16日 23時01分19秒
/dev/pts/2
 ~                                                  2020年07月16日 23時01分35秒
 

RubyにもIOがttyか調べるメソッドが合った。 docs.ruby-lang.org

IO#nochoのソースコードもttyが前提のようなコードだった。

https://github.com/ruby/io-console/blob/a4e0a1d67b6bd61f47f37b810eb261fc18a70967/ext/io/console/console.c#L571